Initial commit.
commit
c230ed684d
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"presets": [
|
||||
["babel-preset-env", {
|
||||
"targets": {
|
||||
"node": true,
|
||||
"uglify": false
|
||||
}
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
"babel-plugin-transform-class-properties",
|
||||
"babel-plugin-transform-runtime",
|
||||
"babel-plugin-transform-object-rest-spread",
|
||||
"babel-plugin-dynamic-import-node"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# Deployed apps should consider commenting this line out:
|
||||
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||
**/node_modules/**
|
||||
!**/node_modules/.gitkeep
|
||||
|
||||
# Webpack output
|
||||
packages/*/dist
|
||||
packages/*/lib
|
||||
|
||||
# Intermediate build files (cache, etc.)
|
||||
/build
|
|
@ -0,0 +1,30 @@
|
|||
extends: airbnb
|
||||
|
||||
parser: babel-eslint
|
||||
|
||||
plugins:
|
||||
- babel
|
||||
- json
|
||||
|
||||
env:
|
||||
browser: true
|
||||
node: true
|
||||
es6: true
|
||||
|
||||
rules:
|
||||
no-console: off
|
||||
no-plusplus:
|
||||
- error
|
||||
- allowForLoopAfterthoughts: true
|
||||
no-underscore-dangle: 'off'
|
||||
react/no-array-index-key: 0
|
||||
|
||||
overrides:
|
||||
- files:
|
||||
- "config/**"
|
||||
- "**/webpack.config*.js"
|
||||
- "**/.babelrc*"
|
||||
rules:
|
||||
import/no-extraneous-dependencies:
|
||||
- error
|
||||
- devDependencies: true
|
|
@ -0,0 +1,33 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# Deployed apps should consider commenting this line out:
|
||||
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||
**/node_modules/**
|
||||
!**/node_modules/.gitkeep
|
||||
|
||||
# Webpack output
|
||||
packages/*/dist
|
||||
packages/*/lib
|
||||
|
||||
# Intermediate build files (cache, etc.)
|
||||
/build
|
|
@ -0,0 +1,4 @@
|
|||
rules:
|
||||
import/no-extraneous-dependencies:
|
||||
- error
|
||||
- devDependencies: true
|
|
@ -0,0 +1,17 @@
|
|||
import { argv, exit } from 'process';
|
||||
import { packageManager, execute } from './tool_env';
|
||||
|
||||
const args = [];
|
||||
|
||||
if (packageManager !== 'npm') {
|
||||
args.push(`--npm-client=${packageManager}`);
|
||||
}
|
||||
|
||||
args.push(...argv.splice(2));
|
||||
|
||||
execute('lerna', args)
|
||||
.then(exit)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
exit(-1);
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
import spawn from 'execa';
|
||||
|
||||
function detectPackageManager() {
|
||||
// @TODO - use a better data source than the user-configurable user agent
|
||||
const userAgent = process.env.npm_config_user_agent;
|
||||
|
||||
// Yarn?
|
||||
if (/\byarn\/[\d.]+\b/.test(userAgent)) {
|
||||
return 'yarn';
|
||||
}
|
||||
|
||||
return 'npm';
|
||||
}
|
||||
|
||||
export const packageManager = detectPackageManager();
|
||||
|
||||
/**
|
||||
* Executes a given command with given argument array and then resolves the
|
||||
* promise with whatever exit code the process returns.
|
||||
*/
|
||||
export function execute(name, args, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// console.log('Environment:', env);
|
||||
console.log('Executing:', [name, ...args]
|
||||
.map(v => (v.includes(' ') ? JSON.stringify(v) : v))
|
||||
.join(' '));
|
||||
|
||||
const proc = spawn(name, args, {
|
||||
stdio: 'inherit',
|
||||
// cwd: undefined,
|
||||
// env,
|
||||
...options,
|
||||
});
|
||||
|
||||
proc.on('error', reject);
|
||||
proc.on('close', resolve);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import { argv, exit } from 'process';
|
||||
import { packageManager, execute } from './tool_env';
|
||||
|
||||
const mappings = {
|
||||
install(...args) {
|
||||
switch (packageManager) {
|
||||
case 'yarn':
|
||||
if (args.length > 0) {
|
||||
return ['add', ...args];
|
||||
}
|
||||
return ['install'];
|
||||
|
||||
default:
|
||||
return ['install', ...args];
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const args = mappings[argv[2]]
|
||||
? mappings[argv[2]]()
|
||||
: argv.slice(2);
|
||||
|
||||
execute(packageManager, args)
|
||||
.then(exit)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
exit(-1);
|
||||
});
|
|
@ -0,0 +1,133 @@
|
|||
import autoprefixer from 'autoprefixer';
|
||||
|
||||
export default class Environment {
|
||||
constructor(options) {
|
||||
this.development = true;
|
||||
this.production = false;
|
||||
this.server = false;
|
||||
|
||||
this.autoprefixerTargets = [
|
||||
'> 1%',
|
||||
'last 4 versions',
|
||||
'Firefox ESR',
|
||||
'ios >= 8',
|
||||
];
|
||||
this.locales = ['en'];
|
||||
|
||||
if (options !== undefined && options !== null) {
|
||||
this.input(options);
|
||||
}
|
||||
}
|
||||
|
||||
input(options) {
|
||||
if (options) {
|
||||
switch (true) {
|
||||
case typeof (options) === 'string': // string
|
||||
this.inputString(options);
|
||||
break;
|
||||
|
||||
case Array.isArray(options): // array
|
||||
options.forEach((arg) => { this.input(arg); });
|
||||
break;
|
||||
|
||||
default: // object
|
||||
Object.keys(options).forEach((k) => {
|
||||
this[k] = options[k] || this[k];
|
||||
});
|
||||
break;
|
||||
}
|
||||
} else if (process.env.NODE_ENV) {
|
||||
this.inputString(process.env.NODE_ENV);
|
||||
}
|
||||
}
|
||||
|
||||
inputString(env) {
|
||||
switch (env.toLowerCase()) {
|
||||
case 'development':
|
||||
this.development = true;
|
||||
this.production = false;
|
||||
break;
|
||||
case 'production':
|
||||
this.development = false;
|
||||
this.production = true;
|
||||
break;
|
||||
case 'server':
|
||||
this.server = true;
|
||||
break;
|
||||
default:
|
||||
console.warn('Unknown environment:', env);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
styleLoaders(...preprocessingLoaders) {
|
||||
const {
|
||||
production,
|
||||
autoprefixerTargets,
|
||||
server,
|
||||
|
||||
ExtractTextPlugin, // @HACK
|
||||
} = this;
|
||||
|
||||
if (!ExtractTextPlugin) {
|
||||
throw new Error('Need a valid ExtractTextPlugin fed into the environment object.');
|
||||
}
|
||||
|
||||
let cssLoaders = [
|
||||
{
|
||||
loader: 'style-loader',
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
sourceMap: true,
|
||||
modules: true,
|
||||
localIdentName: production
|
||||
? '[name]__[local]--[hash:base64:5]'
|
||||
: '[name]__[local]--[hash:base64:5]',
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
ident: 'postcss',
|
||||
plugins: [
|
||||
autoprefixer({
|
||||
browsers: autoprefixerTargets,
|
||||
grid: false,
|
||||
}),
|
||||
],
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
].filter(loader => loader !== false);
|
||||
|
||||
if (preprocessingLoaders && preprocessingLoaders.length > 0) {
|
||||
cssLoaders.push(
|
||||
{
|
||||
loader: 'resolve-url-loader',
|
||||
options: {
|
||||
fail: true,
|
||||
silent: false,
|
||||
},
|
||||
},
|
||||
...preprocessingLoaders.map(loader => Object.assign({}, loader, {
|
||||
options: Object.assign({}, loader.options || {}, {
|
||||
sourceMap: true,
|
||||
}),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
if (!server) {
|
||||
const fallback = cssLoaders.shift();
|
||||
cssLoaders = ExtractTextPlugin.extract({
|
||||
fallback,
|
||||
use: cssLoaders,
|
||||
});
|
||||
}
|
||||
|
||||
return cssLoaders;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"lerna": "2.1.0",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "0.0.1"
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"name": "@eldewrito-rcon/root",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"bootstrap": "babel-node ./config/tools/lerna bootstrap --hoist",
|
||||
"build": "babel-node ./config/tools/lerna run build",
|
||||
"clean": "babel-node ./config/tools/lerna run clean",
|
||||
"lerna": "babel-node ./config/tools/lerna.js",
|
||||
"lint": "eslint .",
|
||||
"postinstall": "babel-node ./config/tools/yarn-or-npm run -s bootstrap",
|
||||
"start": "cd packages/backend && babel-node ../../config/tools/yarn-or-npm start",
|
||||
"test:coverage": "babel-node ./config/tools/lerna run test:coverage",
|
||||
"test:watch": "babel-node ./config/tools/lerna run test:watch",
|
||||
"test": "babel-node ./config/tools/lerna run test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@git.icedream.tech:2222/icedream/eldewrito-rcon.git"
|
||||
},
|
||||
"keywords": [
|
||||
"website",
|
||||
"vizon",
|
||||
"rizon",
|
||||
"draw",
|
||||
"countdown"
|
||||
],
|
||||
"author": "Carl Kittelberger <icedream@icedream.pw>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^7.1.4",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-plugin-dynamic-import-node": "^1.0.2",
|
||||
"babel-plugin-dynamic-import-webpack": "^1.0.1",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-plugin-transform-react-constant-elements": "^6.23.0",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"chalk": "^2.1.0",
|
||||
"debug": "^3.0.1",
|
||||
"eslint": "^4.6.1",
|
||||
"eslint-config-airbnb": "^15.1.0",
|
||||
"eslint-plugin-babel": "^4.1.2",
|
||||
"eslint-plugin-import": "^2.7.0",
|
||||
"eslint-plugin-json": "^1.2.0",
|
||||
"eslint-plugin-jsx-a11y": "^5.1.1",
|
||||
"eslint-plugin-react": "^7.3.0",
|
||||
"execa": "^0.8.0",
|
||||
"lerna": "^2.1.2",
|
||||
"nodemon": "^1.12.0",
|
||||
"primus-multiplex": "^3.2.1",
|
||||
"rimraf": "^2.6.1",
|
||||
"slash": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"performance-now": "^2.1.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"presets": [
|
||||
["babel-preset-env", {
|
||||
"targets": {
|
||||
"node": true,
|
||||
"uglify": false
|
||||
}
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
"babel-plugin-dynamic-import-node",
|
||||
"babel-plugin-syntax-export-extensions",
|
||||
"babel-plugin-transform-class-properties",
|
||||
"babel-plugin-transform-export-extensions",
|
||||
"babel-plugin-transform-object-rest-spread",
|
||||
"babel-plugin-transform-runtime"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# Deployed apps should consider commenting this line out:
|
||||
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
|
||||
node_modules/**
|
||||
!node_modules/.gitkeep
|
||||
|
||||
# Webpack/Babel output
|
||||
/dist
|
||||
/lib
|
||||
|
||||
# Intermediate build files (cache, etc.)
|
||||
/build
|
|
@ -0,0 +1,12 @@
|
|||
overrides:
|
||||
- files:
|
||||
- src/website/**/*
|
||||
rules:
|
||||
import/no-extraneous-dependencies:
|
||||
- error
|
||||
- devDependencies: true
|
||||
|
||||
- files:
|
||||
- src/**/*
|
||||
rules:
|
||||
no-console: error
|
|
@ -0,0 +1,2 @@
|
|||
package-lock=false
|
||||
prune=false
|
|
@ -0,0 +1,134 @@
|
|||
{
|
||||
"name": "@eldewrito-rcon/frontend",
|
||||
"version": "0.0.1",
|
||||
"description": "Black Hole, the game where you avoid getting your numbers sucked in.",
|
||||
"private": true,
|
||||
"browser": "./lib/website/index.js",
|
||||
"main": "./lib/middleware.node/index.js",
|
||||
"jsnext:main": "./lib/middleware.es6/index.js",
|
||||
"module": "./lib/middleware.es6/index.js",
|
||||
"scripts": {
|
||||
"build:website:development": "babel-node ../../config/tools/yarn-or-npm run -s build:website -- --env development",
|
||||
"build:website:production": "babel-node ../../config/tools/yarn-or-npm run -s build:website -- --env production",
|
||||
"build:website:watch": "run-p build:website -- --watch",
|
||||
"build:website": "webpack",
|
||||
"build:production": "bnr build:production",
|
||||
"build": "run-p build:website",
|
||||
"clean": "rimraf build lib",
|
||||
"lint": "eslint .",
|
||||
"prepublishOnly": "babel-node ../../config/tools/yarn-or-npm run -s build:production",
|
||||
"start": "webpack-dev-server --mode development --env development --hot --inline"
|
||||
},
|
||||
"betterScripts": {
|
||||
"build:production": {
|
||||
"command": "yon run -s build",
|
||||
"env": {
|
||||
"NODE_ENV": "production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@git.icedream.tech:2222/icedream/eldewrito-rcon.git"
|
||||
},
|
||||
"keywords": [
|
||||
"website"
|
||||
],
|
||||
"files": [
|
||||
"lib",
|
||||
"README.md"
|
||||
],
|
||||
"author": "Carl Kittelberger <icedream@icedream.pw>",
|
||||
"license": "UNLICENSED",
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^8.3.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-eslint": "^8.2.3",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-plugin-dynamic-import-node": "^1.2.0",
|
||||
"babel-plugin-dynamic-import-webpack": "^1.0.2",
|
||||
"babel-plugin-syntax-export-extensions": "^6.13.0",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-export-extensions": "^6.22.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-plugin-transform-react-constant-elements": "^6.23.0",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"bem-cn": "^2.1.3",
|
||||
"better-npm-run": "^0.1.0",
|
||||
"binary-pack": "^1.0.2",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.1.2",
|
||||
"chalk": "^2.4.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"debug": "^3.1.0",
|
||||
"engine.io-client": "^3.2.1",
|
||||
"engine.io-rooms": "^0.2.0",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-airbnb": "^16.1.0",
|
||||
"eslint-plugin-babel": "^5.1.0",
|
||||
"eslint-plugin-import": "^2.11.0",
|
||||
"eslint-plugin-json": "^1.2.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||
"eslint-plugin-react": "^7.7.0",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"extract-text-webpack-plugin": "4.0.0-beta.0",
|
||||
"file-loader": "^1.1.11",
|
||||
"history": "^4.7.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"moment-timezone": "^0.5.16",
|
||||
"node-sass": "^4.8.3",
|
||||
"nodemon": "^1.17.3",
|
||||
"normalize-scss": "^7.0.1",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"offline-plugin": "^4.9.0",
|
||||
"performance-now": "^2.1.0",
|
||||
"postcss-loader": "^2.1.4",
|
||||
"preact": "^8.2.7",
|
||||
"preact-compat": "^3.18.0",
|
||||
"progress-bar-webpack-plugin": "^1.11.0",
|
||||
"prop-types": "^15.6.1",
|
||||
"react": "^16.3.2",
|
||||
"react-dom": "^16.3.2",
|
||||
"react-fontawesome": "^1.6.1",
|
||||
"react-helmet": "^5.2.0",
|
||||
"react-hot-loader": "^4.1.1",
|
||||
"react-redux": "^5.0.7",
|
||||
"react-router": "^4.2.0",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-router-redux": "^5.0.0-alpha.6",
|
||||
"redux": "^4.0.0",
|
||||
"redux-actions": "^2.3.0",
|
||||
"redux-devtools": "^3.4.1",
|
||||
"redux-devtools-diff-monitor": "^5.0.5",
|
||||
"redux-devtools-dock-monitor": "^1.1.3",
|
||||
"redux-devtools-extension": "^2.13.2",
|
||||
"redux-devtools-log-monitor": "^1.4.0",
|
||||
"redux-promise": "^0.5.3",
|
||||
"redux-promises": "^1.0.0",
|
||||
"resolve-url-loader": "^2.3.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"sass-loader": "^7.0.1",
|
||||
"slash": "^2.0.0",
|
||||
"spinkit": "^1.2.5",
|
||||
"style-loader": "^0.21.0",
|
||||
"stylus": "^0.54.5",
|
||||
"stylus-loader": "^3.0.2",
|
||||
"uglifyjs-webpack-plugin": "^1.2.5",
|
||||
"url-loader": "^1.0.1",
|
||||
"webfontloader": "^1.6.28",
|
||||
"webpack": "^4.6.0",
|
||||
"webpack-cli": "^2.0.15",
|
||||
"webpack-dev-middleware": "^3.1.2",
|
||||
"webpack-dev-server": "^3.1.3",
|
||||
"webpack-hot-middleware": "^2.22.1",
|
||||
"webpack-merge": "^4.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"connect-history-api-fallback": "^1.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": "^4.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"presets": [
|
||||
["babel-preset-env", {
|
||||
"targets": {
|
||||
"browsers": ">1%",
|
||||
"uglify": false
|
||||
},
|
||||
"modules": false
|
||||
}],
|
||||
"babel-preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
"babel-plugin-dynamic-import-node",
|
||||
"babel-plugin-syntax-export-extensions",
|
||||
"babel-plugin-transform-class-properties",
|
||||
"babel-plugin-transform-export-extensions",
|
||||
"babel-plugin-transform-react-constant-elements",
|
||||
["babel-plugin-transform-runtime", {
|
||||
"helpers": false,
|
||||
"polyfill": false,
|
||||
"regenerator": true
|
||||
}]
|
||||
]
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
|
||||
import Client from './Client';
|
||||
import Login from './Login';
|
||||
import Log from './Log';
|
||||
import Input from './Input';
|
||||
|
||||
import VerticalCentering from './ui/VerticalCentering';
|
||||
|
||||
import style from './Application.styl';
|
||||
|
||||
class ApplicationView extends React.Component {
|
||||
constructor(rootContainer) {
|
||||
super();
|
||||
this.rootContainer = rootContainer;
|
||||
this.client = new Client();
|
||||
this.client.on('open', () => {
|
||||
this.setState({
|
||||
isLoggedIn: true,
|
||||
isLoggingIn: false,
|
||||
log: [],
|
||||
});
|
||||
});
|
||||
this.client.on('close', () => {
|
||||
this.setState({
|
||||
isLoggedIn: false,
|
||||
isLoggingIn: false,
|
||||
log: [],
|
||||
});
|
||||
});
|
||||
this.client.on('message', (e) => {
|
||||
this.addLogLine(e, true);
|
||||
});
|
||||
this.client.on('error', e => alert(e));
|
||||
this.state = {
|
||||
isLoggedIn: false,
|
||||
isLoggingIn: false,
|
||||
log: [],
|
||||
};
|
||||
this.handleLoginSubmit = this.handleLoginSubmit.bind(this);
|
||||
this.handleCommandSubmit = this.handleCommandSubmit.bind(this);
|
||||
this.handleDisconnectClick = this.handleDisconnectClick.bind(this);
|
||||
}
|
||||
|
||||
client = null;
|
||||
|
||||
addLogLine(text, isServer) {
|
||||
this.setState(old => ({
|
||||
...old,
|
||||
log: [
|
||||
...old.log,
|
||||
{ text, isServer },
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
||||
handleDisconnectClick() {
|
||||
this.client.close();
|
||||
}
|
||||
|
||||
handleCommandSubmit({
|
||||
command,
|
||||
}) {
|
||||
this.addLogLine(command, false);
|
||||
this.client.send(command);
|
||||
}
|
||||
|
||||
handleLoginSubmit({
|
||||
hostname,
|
||||
password,
|
||||
port = 11776,
|
||||
}) {
|
||||
this.setState({
|
||||
isLoggedIn: false,
|
||||
isLoggingIn: true,
|
||||
});
|
||||
this.client.connect(hostname, { password, port });
|
||||
}
|
||||
|
||||
render() {
|
||||
let content = null;
|
||||
if (!this.state.isLoggedIn) {
|
||||
content = (
|
||||
<Login onSubmit={this.handleLoginSubmit} />
|
||||
);
|
||||
} else if (this.state.isLoggingIn) {
|
||||
content = (
|
||||
<div>Please wait...</div>
|
||||
);
|
||||
} else {
|
||||
// logged in
|
||||
content = (
|
||||
<div className="client">
|
||||
<button onClick={this.handleDisconnectClick}>Disconnect</button>
|
||||
<Input onSubmit={this.handleCommandSubmit} />
|
||||
<Log log={this.state.log} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className={style.app}>
|
||||
<VerticalCentering>
|
||||
{ content }
|
||||
</VerticalCentering>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class Application {
|
||||
constructor(rootContainer) {
|
||||
this.rootContainer = rootContainer;
|
||||
this.elem = <ApplicationView />;
|
||||
}
|
||||
|
||||
renderToContainer() {
|
||||
ReactDOM.render(this.elem, this.rootContainer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
.app
|
||||
height: 100%
|
||||
max-height: 100%
|
||||
width: 100%
|
||||
max-width: 100%
|
||||
font-family: monospace
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
import EventEmitter from 'events';
|
||||
|
||||
export default class Client extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.ws = null;
|
||||
}
|
||||
|
||||
isConnected() {
|
||||
return this.ws != null;
|
||||
}
|
||||
|
||||
connect(hostname, {
|
||||
port = 11776,
|
||||
secure = false,
|
||||
protocols = ['dew-rcon'],
|
||||
password = '',
|
||||
}) {
|
||||
const protocol = secure ? 'wss' : 'ws';
|
||||
const url = `${protocol}://${hostname}:${port}`;
|
||||
const ws = new WebSocket(url, ...protocols);
|
||||
const hasAuthenticated = false;
|
||||
|
||||
ws.onmessage = ({ data }) => {
|
||||
if (!hasAuthenticated) {
|
||||
// authentication result
|
||||
switch (data) {
|
||||
case 'deny':
|
||||
this.emit('error', 'Authentication failed');
|
||||
ws.close();
|
||||
break;
|
||||
case 'accept':
|
||||
this.ws = ws;
|
||||
this.emit('open');
|
||||
break;
|
||||
default:
|
||||
this.emit('message', data);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.emit('message', data);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onopen = () => {
|
||||
ws.send(password);
|
||||
};
|
||||
|
||||
ws.onerror = (err) => {
|
||||
this.emit('error', err);
|
||||
};
|
||||
|
||||
ws.onclose = (close) => {
|
||||
const { reason, ...otherErr } = close;
|
||||
this.emit('close', reason, otherErr);
|
||||
};
|
||||
}
|
||||
|
||||
send(cmd) {
|
||||
this.ws.send(cmd);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.ws.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const initialState = {
|
||||
result: {
|
||||
command: '',
|
||||
},
|
||||
};
|
||||
|
||||
export default class Input extends React.Component {
|
||||
static propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleFormValueChange = {
|
||||
command: this.handleFormValueChange.bind(this, 'command'),
|
||||
};
|
||||
|
||||
this.state = initialState;
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit(this.state.result);
|
||||
this.setState(initialState);
|
||||
}
|
||||
|
||||
handleFormValueChange(name, {
|
||||
target: {
|
||||
value,
|
||||
},
|
||||
}) {
|
||||
this.setState(old => ({
|
||||
...old,
|
||||
result: {
|
||||
...old.result,
|
||||
[name]: value,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="input">
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input type="text" value={this.state.result.command} name="command" onChange={this.handleFormValueChange.command} />
|
||||
<input type="submit" value="OK" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import React from 'react';
|
||||
|
||||
import style from './Log.styl';
|
||||
|
||||
const LogItem = ({
|
||||
isServer,
|
||||
children,
|
||||
}) => (
|
||||
<div className={[
|
||||
'line',
|
||||
isServer
|
||||
? 'line-by-server'
|
||||
: 'line-by-client',
|
||||
]
|
||||
.map(n => style[n])
|
||||
.join(' ')}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default class Log extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className={style.log}>
|
||||
{this.props.log.map(({
|
||||
isServer,
|
||||
text,
|
||||
}, index) => (
|
||||
<LogItem isServer={isServer}>{text}</LogItem>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
.log
|
||||
font-family: monospace
|
||||
white-space: pre-wrap
|
||||
|
||||
// .line
|
||||
|
||||
.line-by-client
|
||||
color: grey
|
||||
|
||||
// .line-by-server
|
|
@ -0,0 +1,69 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import style from './Login.styl';
|
||||
|
||||
export default class Login extends React.Component {
|
||||
static propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleFormValueChange = {
|
||||
hostname: this.handleFormValueChange.bind(this, 'hostname'),
|
||||
password: this.handleFormValueChange.bind(this, 'password'),
|
||||
port: this.handleFormValueChange.bind(this, 'port'),
|
||||
};
|
||||
|
||||
this.state = {
|
||||
result: {
|
||||
hostname: '',
|
||||
password: '',
|
||||
port: 11776,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit(this.state.result);
|
||||
}
|
||||
|
||||
handleFormValueChange(name, {
|
||||
target: {
|
||||
value,
|
||||
},
|
||||
}) {
|
||||
this.setState(old => ({
|
||||
...old,
|
||||
result: {
|
||||
...old.result,
|
||||
[name]: value,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={style.loginBox}>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div>
|
||||
<label className={style.label} htmlFor="rconServerName">Hostname:</label>
|
||||
<input className={style.input} type="text" value={this.state.result.hostname} name="rconServerName" onChange={this.handleFormValueChange.hostname} />
|
||||
</div>
|
||||
<div>
|
||||
<label className={style.label} htmlFor="rconPort">Port:</label>
|
||||
<input className={style.input} type="number" value={this.state.result.port} name="rconPort" onChange={this.handleFormValueChange.port} />
|
||||
</div>
|
||||
<div>
|
||||
<label className={style.label} htmlFor="rconPassword">Password:</label>
|
||||
<input className={style.input} type="password" value={this.state.result.password} name="rconPassword" onChange={this.handleFormValueChange.password} />
|
||||
</div>
|
||||
<input className={style.submit} type="submit" value="OK" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
.loginBox
|
||||
width: 520px
|
||||
height: 180px
|
||||
text-align: center
|
||||
border: grey 1px solid
|
||||
border-radius: 5px
|
||||
padding: 8px
|
||||
|
||||
.submit
|
||||
width: 100%
|
||||
|
||||
.label
|
||||
width: 25%
|
||||
clear: left
|
||||
text-align: right
|
||||
|
||||
.input, .label
|
||||
float: left
|
|
@ -0,0 +1,21 @@
|
|||
$debug: false
|
||||
|
||||
@if ($debug)
|
||||
*
|
||||
background-color: rgba(255, 0, 255, 0.1)
|
||||
border: red 1px solid
|
||||
box-sizing: border-box
|
||||
position: relative
|
||||
&:after
|
||||
content: attr(class)
|
||||
z-index: 99999
|
||||
box-shadow: 0 0 10px black
|
||||
position: absolute
|
||||
left: 0
|
||||
top: 0
|
||||
background: white
|
||||
border: black 1px solid
|
||||
padding: 4px
|
||||
font-size: 12px
|
||||
font-weight: bold
|
||||
font-family: monospace
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<title>ElDewrito RCON Client</title>
|
||||
<meta name="description" content="">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>This website requires JavaScript, please enable it.</noscript>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
// import * as OfflinePluginRuntime from 'offline-plugin/runtime';
|
||||
|
||||
import Application from './Application';
|
||||
|
||||
import './index.sass';
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`%cThis is a ${process.env.NODE_ENV} build!`,
|
||||
'font-family: sans-serif; color: black; background-color: yellow; font-size: 2.5em; font-weight: bold');
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('%cThis code MUST not be deployed in production!',
|
||||
'font-family: sans-serif; color: black; background-color: yellow; font-weight: bold; font-size: 1.5em');
|
||||
}
|
||||
|
||||
// Offline caching runtime
|
||||
// OfflinePluginRuntime.install();
|
||||
|
||||
// Create div node to render app in
|
||||
const rootContainer = document.createElement('div');
|
||||
document.body.appendChild(rootContainer);
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const app = new Application(rootContainer);
|
||||
app.renderToContainer();
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
@import ~normalize-scss/sass/normalize
|
||||
|
||||
@import debug
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import style from './VerticalCentering.styl';
|
||||
|
||||
const VerticalCentering = ({ children }) => (
|
||||
<div className={style.vcenterWrapper}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
VerticalCentering.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default VerticalCentering;
|
|
@ -0,0 +1,3 @@
|
|||
.vcenterWrapper
|
||||
display: flex
|
||||
align-items: center
|
|
@ -0,0 +1,321 @@
|
|||
import {
|
||||
DefinePlugin,
|
||||
NamedModulesPlugin,
|
||||
HashedModuleIdsPlugin,
|
||||
LoaderOptionsPlugin,
|
||||
HotModuleReplacementPlugin,
|
||||
NoEmitOnErrorsPlugin,
|
||||
|
||||
optimize,
|
||||
} from 'webpack';
|
||||
|
||||
import path from 'path';
|
||||
import { isatty } from 'tty';
|
||||
import chalk from 'chalk';
|
||||
import _debug from 'debug';
|
||||
// import slash from 'slash';
|
||||
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
|
||||
import ExtractTextPlugin from 'extract-text-webpack-plugin';
|
||||
import HtmlPlugin from 'html-webpack-plugin';
|
||||
import ProgressBarPlugin from 'progress-bar-webpack-plugin';
|
||||
import UglifyJsPlugin from 'uglifyjs-webpack-plugin';
|
||||
|
||||
import Environment from '../../config/webpack/environment';
|
||||
|
||||
const debug = _debug('webpack:config');
|
||||
debug.generated = _debug('webpack:config:generated');
|
||||
|
||||
debug.generated('filename:', __filename);
|
||||
debug.generated('dirname:', __dirname);
|
||||
|
||||
|
||||
const {
|
||||
ModuleConcatenationPlugin,
|
||||
} = optimize;
|
||||
|
||||
const websiteSubfolder = 'website';
|
||||
|
||||
export default (options) => {
|
||||
const environment = new Environment({
|
||||
ExtractTextPlugin, // @HACK
|
||||
});
|
||||
|
||||
environment.input(options);
|
||||
|
||||
debug.generated(environment);
|
||||
|
||||
const {
|
||||
development,
|
||||
production,
|
||||
server,
|
||||
autoprefixerTargets,
|
||||
} = environment;
|
||||
|
||||
const baseOutputFilename = development
|
||||
? 'assets/dev/[name].dev.[ext]'
|
||||
// Always use a hash (in production) to prevent files with the same name causing issues
|
||||
: 'assets/prod/[chunkhash:2]/[name].[chunkhash:8].[ext]';
|
||||
|
||||
const webpackChunkFilename = baseOutputFilename
|
||||
.replace(/\[ext(.*?)\]/g, 'js');
|
||||
const webpackOutputFilename = webpackChunkFilename;
|
||||
|
||||
const assetOutputFilename = baseOutputFilename
|
||||
.replace(/\[chunkhash(.*?)\]/g, '[hash$1]');
|
||||
|
||||
const serviceWorkerFilename = assetOutputFilename
|
||||
.replace(/\[name\]/g, 'sw')
|
||||
.replace(/\[ext\]/g, 'js');
|
||||
|
||||
const cssOutputFileName = baseOutputFilename
|
||||
.replace(/\[ext(.*?)\]/g, 'css')
|
||||
.replace(/\[chunkhash(.*?)\]/g, '[contenthash$1]');
|
||||
// const cssOutputRebasePath = `${slash(path.relative(path.dirname(cssOutputFileName), ''))}/`;
|
||||
const cssOutputRebasePath = '/';
|
||||
|
||||
// Default options for url-loader
|
||||
const urlLoaderOptions = {
|
||||
limit: 1, // Don't inline anything (but empty files) by default
|
||||
name: assetOutputFilename,
|
||||
publicPath: cssOutputRebasePath,
|
||||
};
|
||||
|
||||
const config = {
|
||||
name: 'frontend',
|
||||
target: 'web',
|
||||
|
||||
devServer: {
|
||||
// inline: true,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
// historyApiFallback: {
|
||||
// index: '/',
|
||||
// },
|
||||
hot: true,
|
||||
noInfo: true,
|
||||
overlay: true,
|
||||
publicPath: '',
|
||||
quiet: false,
|
||||
watchOptions: {
|
||||
ignored: /node_modules/,
|
||||
},
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules/,
|
||||
options: {
|
||||
// Look for babel configuration in project directory
|
||||
babelrc: false,
|
||||
// Cache transformations to the filesystem (in default temp dir)
|
||||
cacheDirectory: true,
|
||||
|
||||
presets: [
|
||||
['babel-preset-env', {
|
||||
targets: {
|
||||
browsers: autoprefixerTargets,
|
||||
uglify: false,
|
||||
},
|
||||
// spec: true,
|
||||
// debug: development,
|
||||
modules: false, // do not transpile modules, webpack 2+ does that
|
||||
}],
|
||||
'babel-preset-react',
|
||||
],
|
||||
plugins: [
|
||||
'babel-plugin-transform-react-constant-elements',
|
||||
'babel-plugin-transform-class-properties',
|
||||
'babel-plugin-transform-object-rest-spread',
|
||||
['babel-plugin-transform-runtime', {
|
||||
helpers: false,
|
||||
polyfill: false,
|
||||
regenerator: true,
|
||||
}],
|
||||
'babel-plugin-dynamic-import-webpack',
|
||||
],
|
||||
},
|
||||
},
|
||||
...[
|
||||
/\.(gif|png|webp)$/i, // graphics
|
||||
/\.svg$/i, // svg
|
||||
/\.jpe?g$/i, // jpeg
|
||||
/\.(mp4|ogg|webm)$/i, // video
|
||||
/\.(eot|otf|ttf|woff|woff2)$/i, // fonts
|
||||
/\.(wav|mp3|m4a|aac|oga)$/i, // audio
|
||||
].map(test => ({
|
||||
test,
|
||||
loader: 'url-loader',
|
||||
options: urlLoaderOptions,
|
||||
})),
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: environment.styleLoaders(),
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/,
|
||||
use: environment.styleLoaders(
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
test: /\.styl$/,
|
||||
use: environment.styleLoaders(
|
||||
{
|
||||
loader: 'stylus-loader',
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
strictExportPresence: true,
|
||||
},
|
||||
|
||||
output: {
|
||||
filename: webpackOutputFilename,
|
||||
chunkFilename: webpackChunkFilename,
|
||||
path: path.join(__dirname, 'lib', websiteSubfolder),
|
||||
publicPath: '',
|
||||
},
|
||||
|
||||
plugins: [
|
||||
// Show progress as a bar during build
|
||||
isatty(1) && new ProgressBarPlugin({
|
||||
complete: chalk.white('\u2588'),
|
||||
incomplete: chalk.grey('\u2591'),
|
||||
format: `:bar ${chalk.cyan.bold(':percent')} Webpack build: ${chalk.grey(':msg')}`,
|
||||
}),
|
||||
|
||||
// Enforce case-sensitive import paths
|
||||
new CaseSensitivePathsPlugin(),
|
||||
// Replace specified expressions with values
|
||||
new DefinePlugin({
|
||||
'process.env.__DEV__': JSON.stringify(development),
|
||||
'process.env.__PROD__': JSON.stringify(production),
|
||||
'process.env.__SERVER__': JSON.stringify(server),
|
||||
'process.env.NODE_ENV': JSON.stringify(production ? 'production' : 'development'),
|
||||
}),
|
||||
|
||||
// Dev server build
|
||||
...[
|
||||
// Hot module reloading
|
||||
new HotModuleReplacementPlugin(),
|
||||
new NoEmitOnErrorsPlugin(),
|
||||
|
||||
// Use paths as names when serving
|
||||
new NamedModulesPlugin(),
|
||||
].filter(() => server),
|
||||
|
||||
// If we're not serving, we're creating a static build
|
||||
...[// Extract imported stylesheets out into .css files
|
||||
new ExtractTextPlugin({
|
||||
allChunks: true,
|
||||
filename: cssOutputFileName,
|
||||
}),
|
||||
].filter(() => !server),
|
||||
|
||||
// Move modules imported from node_modules/ into a vendor chunk when enabled
|
||||
// FIXME - this plugin has been removed, use new option from Webpack 4
|
||||
// new CommonsChunkPlugin({
|
||||
// name: 'vendor',
|
||||
// minChunks(module) {
|
||||
// return (module.resource && module.resource.includes('node_modules'));
|
||||
// },
|
||||
// }),
|
||||
|
||||
// If we're generating an HTML file, we must be building a web app, so
|
||||
// configure deterministic hashing for long-term caching.
|
||||
|
||||
// Generate stable module ids instead of having Webpack assign integers.
|
||||
// NamedModulesPlugin allows for easier debugging and
|
||||
// HashedModuleIdsPlugin does this without adding too much to bundle
|
||||
// size.
|
||||
development
|
||||
? new NamedModulesPlugin()
|
||||
: new HashedModuleIdsPlugin(),
|
||||
|
||||
// Production builds
|
||||
...[
|
||||
// JavaScript minification
|
||||
new LoaderOptionsPlugin({ debug: false, minimize: true }),
|
||||
|
||||
// Hoisting
|
||||
new ModuleConcatenationPlugin(),
|
||||
].filter(() => production),
|
||||
|
||||
new HtmlPlugin({
|
||||
template: path.join(__dirname, 'src', websiteSubfolder, 'index.html'),
|
||||
filename: 'index.html',
|
||||
hash: false,
|
||||
inject: true,
|
||||
compile: true,
|
||||
favicon: false,
|
||||
minify: production ? {
|
||||
removeComments: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
useShortDoctype: true,
|
||||
// includeAutoGeneratedTags: false,
|
||||
collapseWhitespace: true,
|
||||
// conservativeCollapse: true,
|
||||
} : false,
|
||||
cache: true,
|
||||
showErrors: true,
|
||||
chunks: 'all',
|
||||
excludeChunks: [],
|
||||
title: 'VIzon Countdown',
|
||||
xhtml: false,
|
||||
chunksSortMode: 'dependency',
|
||||
}),
|
||||
|
||||
].filter(plugin => plugin !== false),
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.js', '.json', '.jsx',
|
||||
],
|
||||
alias: {
|
||||
'moment-timezone': 'moment-timezone/builds/moment-timezone-with-data-2012-2022.js',
|
||||
},
|
||||
},
|
||||
resolveLoader: {
|
||||
modules: ['node_modules', 'nwb'],
|
||||
},
|
||||
optimization: {
|
||||
minimize: production,
|
||||
minimizer: [
|
||||
new UglifyJsPlugin({
|
||||
parallel: true,
|
||||
uglifyOptions: {
|
||||
compress: {
|
||||
warnings: false,
|
||||
},
|
||||
output: {
|
||||
comments: false,
|
||||
},
|
||||
},
|
||||
sourceMap: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
devtool: server ? 'cheap-module-source-map' : 'source-map',
|
||||
entry: {
|
||||
app: [
|
||||
...[
|
||||
'eventsource-polyfill',
|
||||
'react-hot-loader/patch',
|
||||
'webpack-hot-middleware/client',
|
||||
].filter(() => server).map(require.resolve),
|
||||
path.join(__dirname, 'src', websiteSubfolder),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
debug.generated(config);
|
||||
|
||||
return config;
|
||||
};
|
Loading…
Reference in New Issue