diff --git a/.travis.yml b/.travis.yml index e573baac..1fe8f706 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,8 @@ cache: - node_modules before_script: - - npm install -g gulp - - npm install -g bower + script: - - gulp lint - - gulp build-prod - - gulp test \ No newline at end of file + - npm run lint + - npm test + - npm run build \ No newline at end of file diff --git a/devServer.js b/devServer.js index 4ef236a0..1e65a9ff 100644 --- a/devServer.js +++ b/devServer.js @@ -1,27 +1,15 @@ -var path = require('path'); -var express = require('express'); var webpack = require('webpack'); +var WebpackDevServer = require("webpack-dev-server"); var config = require('./webpack.config.dev'); -var app = express(); -var compiler = webpack(config); - -app.use(require('webpack-dev-middleware')(compiler, { - noInfo: true, - publicPath: config.output.publicPath -})); - -app.use(require('webpack-hot-middleware')(compiler)); - -app.get('*', function(req, res) { - res.sendFile(path.join(__dirname, 'src/index.html')); -}); - -app.listen(3300, 'localhost', function(err) { +new WebpackDevServer(webpack(config), { + publicPath: config.output.publicPath, + hot: true, + historyApiFallback: true +}).listen(3300, "0.0.0.0", function (err, result) { if (err) { console.log(err); - return; } - console.log('Listening at http://localhost:3000'); -}); \ No newline at end of file + console.log("Listening at localhost:3300"); +}); diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 00000000..75916e93 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,59 @@ +worker_processes 2; +error_log ./nginx.error.log; +worker_rlimit_nofile 8192; +pid nginx.pid; + +events { + worker_connections 1024; + multi_accept on; +} + +http { + + access_log off; + charset UTF-8; + + types { + text/html html htm shtml; + text/css css; + text/xml xml rss; + image/gif gif; + image/jpeg jpeg jpg; + application/x-javascript js; + text/plain txt; + image/png png; + image/svg+xml svg; + image/x-icon ico; + application/pdf pdf; + text/cache-manifest appcache; + } + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + server { + listen 3300; + server_name localhost; + root ./build/; + index index.html; + + location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ { + expires -1; + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Credentials true; + add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; + add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + access_log off; + } + + location / { + try_files $uri $uri/ /index.html =404; + } + } +} + diff --git a/package.json b/package.json index 9ac152b2..78966574 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,16 @@ "license": "MIT", "scripts": { "clean": "rimraf build", - "build:prod": "NODE_ENV=production webpack --config webpack.config.prod.js", - "build": "npm run clean && npm run build:prod", "start": "node devServer.js", - "lint": "eslint src" + "lint": "eslint ./src", + "prod-serve": "nginx -p $(pwd) -c nginx.conf", + "prod-stop": "kill -QUIT $(cat nginx.pid)", + "build": "npm run clean && NODE_ENV=production webpack -d -p --config webpack.config.prod.js", + "rsync": "rsync -e 'ssh -i $CORIOLIS_PEM' -a --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/www", + "deploy": "npm run lint && npm run build && npm run rsync" }, "devDependencies": { + "appcache-webpack-plugin": "^1.2.1", "babel-core": "^5.4.7", "babel-eslint": "^3.1.9", "babel-loader": "^5.1.2", @@ -27,11 +31,10 @@ "eslint-plugin-react": "^2.3.0", "express": "^4.13.3", "file-loader": "^0.8.4", - "install": "^0.3.0", + "html-webpack-plugin": "^1.7.0", "json-loader": "^0.5.3", "less": "^2.5.3", "less-loader": "^2.2.1", - "npm": "^3.4.0", "react-transform-catch-errors": "^1.0.0", "react-transform-hmr": "^1.0.0", "redbox-react": "^1.0.1", @@ -39,12 +42,12 @@ "style-loader": "^0.13.0", "url-loader": "^0.5.6", "webpack": "^1.9.6", - "webpack-dev-middleware": "^1.2.0", - "webpack-hot-middleware": "^2.0.0" + "webpack-dev-server": "^1.14.0" }, "dependencies": { "classnames": "^2.2.0", "d3": "^3.5.9", + "extract-text-webpack-plugin": "^0.9.1", "fbemitter": "^2.0.0", "lz-string": "^1.4.4", "react": "^0.14.2", diff --git a/src/app/components/AvailableModulesMenu.jsx b/src/app/components/AvailableModulesMenu.jsx index 95bb4bc5..68fc3312 100644 --- a/src/app/components/AvailableModulesMenu.jsx +++ b/src/app/components/AvailableModulesMenu.jsx @@ -24,7 +24,6 @@ export default class AvailableModulesMenu extends TranslatedComponent { for (let i = 0; i < modules.length; i++) { let m = modules[i]; - let classRating = m.class + m.rating; let mount = null; let classes = cn(m.name ? 'lc' : 'c', { active: mountedModule && mountedModule.id === m.id, @@ -87,7 +86,6 @@ export default class AvailableModulesMenu extends TranslatedComponent { list.push(
{translate('empty')}
); for (let g in modules) { let grp = modules[g]; - let grpCode = grp[Object.keys(grp)[0]].grp; // Nasty operation to get the grp property of the first/any single component list.push(
{translate(g)}
); list.push(buildGroup(g, modules[g])); } diff --git a/src/app/components/StandardSlotSection.jsx b/src/app/components/StandardSlotSection.jsx index 1902c3b8..227c2865 100644 --- a/src/app/components/StandardSlotSection.jsx +++ b/src/app/components/StandardSlotSection.jsx @@ -36,7 +36,7 @@ export default class StandardSlotSection extends SlotSection { } _getSlots() { - let { formats, translate, units } = this.context.language; + let { translate, units } = this.context.language; let slots = new Array(8); let open = this._openMenu; let select = this._selectModule; @@ -45,7 +45,6 @@ export default class StandardSlotSection extends SlotSection { let st = ship.standard; let avail = ship.getAvailableModules().standard; let bulkheads = ship.bulkheads; - let bulkheadIndex = bulkheads.id; let currentMenu = this.state.currentMenu; slots[0] = ( @@ -61,11 +60,11 @@ export default class StandardSlotSection extends SlotSection { {currentMenu === bulkheads &&
e.stopPropagation() }>
} diff --git a/src/app/pages/ShipyardPage.jsx b/src/app/pages/ShipyardPage.jsx index 8cfab978..aede2f8f 100755 --- a/src/app/pages/ShipyardPage.jsx +++ b/src/app/pages/ShipyardPage.jsx @@ -136,6 +136,7 @@ export default class ShipyardPage extends Page { } render() { + let translate = this.context.language.translate; let shipSummaries = this.shipSummaries; let shipPredicate = this.state.shipPredicate; let shipPredicateIndex = this.state.shipPredicateIndex; @@ -170,11 +171,6 @@ export default class ShipyardPage extends Page { } }); - let formats = this.context.language.formats; - let fInt = formats.int; - let fRound = formats.round; - let translate = this.context.language.translate; - for (let s of shipSummaries) { shipRows.push(s.rowElement); } diff --git a/src/index.html b/src/index.html index 6774acfd..f08a1ca9 100755 --- a/src/index.html +++ b/src/index.html @@ -1,76 +1,77 @@ - - - Coriolis - - - - - - - + + + Coriolis + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - -
- + + + + + + + +
+ - - - - + ga('create', '{%= o.htmlWebpackPlugin.options.uaTracking %}', 'auto'); + var GAPI_KEY = '{%= o.htmlWebpackPlugin.options.gapiKey %}'; + + {% } %} + diff --git a/webpack.config.dev.js b/webpack.config.dev.js index 875b0c6a..b629a9ac 100644 --- a/webpack.config.dev.js +++ b/webpack.config.dev.js @@ -1,10 +1,13 @@ var path = require('path'); var webpack = require('webpack'); +var pkgJson = require('./package'); +var HtmlWebpackPlugin = require("html-webpack-plugin"); +var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { devtool: 'eval', entry: { - app: [ 'webpack-hot-middleware/client', './src/app/index' ], + app: [ 'webpack-dev-server/client?http://localhost:3300', 'webpack/hot/only-dev-server', path.join(__dirname, "src/app/index.js") ], lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string'] }, resolve: { @@ -12,7 +15,7 @@ module.exports = { extensions: ['', '.js', '.jsx', '.json', '.less'], alias: { 'coriolis-data': path.resolve(__dirname, 'node_modules/coriolis-data') - }, + } }, output: { path: path.join(__dirname, 'build'), @@ -21,20 +24,30 @@ module.exports = { }, plugins: [ new webpack.optimize.CommonsChunkPlugin('lib', 'lib.js'), + new HtmlWebpackPlugin({ + inject: false, + template: path.join(__dirname, "src/index.html"), + cdn: '', + version: pkgJson.version + }), + new ExtractTextPlugin('app.css', { + allChunks: true + }), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() ], module: { loaders: [ - { test: /\.css$/, loader: 'style-loader!css-loader' }, + { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader','css-loader') }, + { test: /\.less$/, loader: ExtractTextPlugin.extract('style-loader','css-loader!less-loader') }, { test: /\.(js|jsx)$/, loaders: [ 'babel' ], include: path.join(__dirname, 'src') }, - { test: /\.less$/, loader: 'style!css!less' }, { test: /\.json$/, loader: 'json-loader' }, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' }, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' }, - { test: /\.(png|jpg|jpeg|gif)?$/, loader: 'file' }, + { test: /\.(png|jpg|jpeg|gif|ico)$/, loader: 'file-loader?name=/images/[name].[ext]' }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' }, + { test: /\.xml$/, loader: 'file' }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' } ] } diff --git a/webpack.config.prod.js b/webpack.config.prod.js index 393a809c..0e770363 100644 --- a/webpack.config.prod.js +++ b/webpack.config.prod.js @@ -1,5 +1,10 @@ var path = require('path'); +var exec = require('child_process').exec; var webpack = require('webpack'); +var pkgJson = require('./package'); +var HtmlWebpackPlugin = require("html-webpack-plugin"); +var ExtractTextPlugin = require("extract-text-webpack-plugin"); +var AppCachePlugin = require('appcache-webpack-plugin'); var node_modules_dir = path.resolve(__dirname, 'node_modules'); var d3Path = path.resolve(node_modules_dir, 'd3/d3.min.js'); @@ -7,8 +12,20 @@ var reactPath = path.resolve(node_modules_dir, 'react/dist/react.min.js'); var reactDomPath = path.resolve(node_modules_dir, 'react-dom/dist/react-dom.min.js'); var lzStringPath = path.resolve(node_modules_dir, 'lz-string/libs/lz-string.min.js'); +function CopyDirPlugin(source, destination) { + this.source = source; + this.destination = destination; +} +CopyDirPlugin.prototype.apply = function(compiler) { + compiler.plugin('done', function() { + console.log(compiler.outputPath, this.destination); + exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination)); + }.bind(this)); +}; + + module.exports = { - entry: { + entry: { app: path.resolve(__dirname, 'src/app/index'), lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string'] }, @@ -24,34 +41,60 @@ module.exports = { }, output: { path: path.join(__dirname, 'build'), - filename: 'app.js' + filename: '[name].[hash:6].js', + publicPath: '//cdn.coriolis.io/' }, plugins: [ - new webpack.optimize.CommonsChunkPlugin('lib', 'lib.js'), + new webpack.optimize.UglifyJsPlugin({ + mangle: { + except: [] + }, + 'screw-ie8': true + }), + new webpack.optimize.CommonsChunkPlugin('lib', 'lib.[hash:6].js'), new HtmlWebpackPlugin({ - inject: true, - template: path.join(__dirname, "src/public/index.html"), - favicon: path.join(__dirname, "src/assets/images/favicon.png"), + inject: false, + appCache: 'coriolis.appcache' minify: { - removeComments: true, + collapseBooleanAttributes: true, collapseWhitespace: true, - minifyJS: true - } + removeAttributeQuotes: true, + removeComments: true, + removeEmptyAttributes: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true + }, + template: path.join(__dirname, "src/index.html"), + uaTracking: process.env.CORIOLIS_UA_TRACKING || '', + gapiKey: process.env.CORIOLIS_GAPI_KEY || '', + version: pkgJson.version + }), + new ExtractTextPlugin('[contenthash:6].css', { + allChunks: true + }), + new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'), + new AppCachePlugin({ + network: ['*'], + settings: ['prefer-online'], + exclude: ['index.html', /.*\.map$/], + output: 'coriolis.appcache' }) ], module: { - noParse: [d3Path, reactPath, reactDomPath, lzStringPath], + noParse: [d3Path, reactPath, lzStringPath], loaders: [ - { test: /\.css$/, loader: 'style-loader!css-loader' }, - { test: /\.(js|jsx)$/, loader: 'babel', include: path.join(__dirname, 'src') }, - { test: /\.less$/, loader: 'style!css!less' }, + { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader','css-loader') }, + { test: /\.less$/, loader: ExtractTextPlugin.extract('style-loader','css-loader!less-loader') }, + { test: /\.(js|jsx)$/, loaders: [ 'babel' ], include: path.join(__dirname, 'src') }, { test: /\.json$/, loader: 'json-loader' }, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' }, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' }, - { test: /\.(png|jpg|jpeg|gif)?$/, loader: 'file' }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' }, - { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' } + { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' }, + { test: /\.xml$/, loader: 'file' }, + { test: /\.(png|jpg|jpeg|gif|ico)$/, loader: 'file-loader?name=/images/[name].[hash:6].[ext]' } ] } };