From bd2895afa01228240eed89a76c3406b09f8ba0fa Mon Sep 17 00:00:00 2001 From: Brett Sun Date: Thu, 2 Jun 2016 15:24:16 +0200 Subject: [PATCH] Add webpack config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Notable changes: * Updated to babel 6 * Updated polyfills to only include core-js/es6 and core-js/stage/4 rather than entire babel/polyfill set * Dev server with hot reloading replaces server.js in development * Updated bootstrap loading to be separate from app’s stylesheets * Cleaned up some of the font dependencies, removing the need for templating their paths --- .bootstraprc | 16 ++ .gitignore | 1 + README.md | 5 +- js/app.js | 21 ++- .../react_s3_fine_uploader.js | 3 +- .../react_s3_fine_uploader_utils.js | 3 +- js/constants/application_constants.js | 14 +- package.json | 33 +++- sass/ascribe-fonts/ascribe-fonts.scss | 10 +- sass/bootstrap/_overrides.scss | 2 + sass/glyphicons-social.scss | 14 +- sass/main.scss | 3 - server.dev.js | 46 +++++ webpack.config.js | 163 ++++++++++++++++++ 14 files changed, 291 insertions(+), 43 deletions(-) create mode 100644 .bootstraprc create mode 100644 sass/bootstrap/_overrides.scss create mode 100644 server.dev.js create mode 100644 webpack.config.js diff --git a/.bootstraprc b/.bootstraprc new file mode 100644 index 00000000..2267939a --- /dev/null +++ b/.bootstraprc @@ -0,0 +1,16 @@ +--- +bootstrapVersion: 3 + +# Phase this out, first with react-component's bootstrap overrides, and then nothing at all +preBootstrapCustomizations: ./sass/bootstrap/overrides + +styleLoaders: + - style + - css?sourceMap + - postcss + - sass?sourceMap&output=expanded&precision=8 + +# For now, just include everything from Bootstrap +styles: true + +scripts: false diff --git a/.gitignore b/.gitignore index d8fcf519..492658f0 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ results .env build/* +dist/* gemini-coverage/* gemini-report/* diff --git a/README.md b/README.md index 0b24374f..b9a7e2b5 100644 --- a/README.md +++ b/README.md @@ -170,12 +170,11 @@ A: Use `npm dedupe` to remove duplicates in npm. This might fix that you're not Q: How can I use a local copy of SPOOL and Onion? A: Easily by starting the your gulp process with the following command: ``` -ONION_BASE_URL='/' ONION_SERVER_URL='http://localhost.com:8000/' gulp serve +ONION_SERVER_URL='http://localhost.com:8000/' gulp serve ``` -Or, by adding these two your environment variables: +Or, by adding the local server url to your environment variables: ``` -ONION_BASE_URL='/' ONION_SERVER_URL='http://localhost.com:8000/' ``` diff --git a/js/app.js b/js/app.js index dc8e3d62..ce7c90b6 100644 --- a/js/app.js +++ b/js/app.js @@ -1,14 +1,14 @@ 'use strict'; -import 'babel/polyfill'; +import 'core-js/es6'; +import 'core-js/stage/4'; import 'classlist-polyfill'; +import 'isomorphic-fetch'; import React from 'react'; import { Router, Redirect } from 'react-router'; import history from './history'; -import fetch from 'isomorphic-fetch'; - import ApiUrls from './constants/api_urls'; import AppConstants from './constants/application_constants'; @@ -20,15 +20,20 @@ import { getDefaultSubdomainSettings, getSubdomainSettings } from './utils/const import { initLogging } from './utils/error_utils'; import { getSubdomain } from './utils/general_utils'; +// FIXME: redo these event actions import EventActions from './actions/event_actions'; +// FIXME: use a ./third_party/index.js instead, remove DebugHandler // You can comment out the modules you don't need // import DebugHandler from './third_party/debug_handler'; -import FacebookHandler from './third_party/facebook_handler'; -import GoogleAnalyticsHandler from './third_party/ga_handler'; -import IntercomHandler from './third_party/intercom_handler'; -import NotificationsHandler from './third_party/notifications_handler'; -import RavenHandler from './third_party/raven_handler'; +import './third_party/facebook_handler'; +import './third_party/ga_handler'; +import './third_party/intercom_handler'; +import './third_party/notifications_handler'; +import './third_party/raven_handler'; + +// Import global stylesheet +import '../sass/main.scss'; const AppGateway = { diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader.js b/js/components/ascribe_uploader/react_s3_fine_uploader.js index e9203d99..1a3baf38 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader.js @@ -1,7 +1,8 @@ 'use strict'; import React from 'react/addons'; -import fineUploader from 'fineUploader'; +// FIXME: remove once using react-components +import fineUploader from 'exports?qq!./vendor/s3.fine-uploader'; import Q from 'q'; import S3Fetcher from '../../fetchers/s3_fetcher'; diff --git a/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js b/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js index d8925748..3b9b94a9 100644 --- a/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js +++ b/js/components/ascribe_uploader/react_s3_fine_uploader_utils.js @@ -1,6 +1,7 @@ 'use strict'; -import fineUploader from 'fineUploader'; +// FIXME: remove once using react-components +import fineUploader from 'exports?qq!./vendor/s3.fine-uploader'; import MimeTypes from '../../constants/mime_types'; diff --git a/js/constants/application_constants.js b/js/constants/application_constants.js index fd73e568..88e0133d 100644 --- a/js/constants/application_constants.js +++ b/js/constants/application_constants.js @@ -1,19 +1,13 @@ 'use strict'; -//const baseUrl = 'http://localhost:8000/api/'; - -//FIXME: referring to a global variable in `window` is not -// super pro. What if we render stuff on the server? -// - super-bro - Senor Developer, 14th July 2015 -//const baseUrl = window.BASE_URL; -const apiEndpoint = window.API_ENDPOINT; -const serverUrl = window.SERVER_URL; -const baseUrl = window.BASE_URL; +const apiEndpoint = process.env.API_URL; +const serverUrl = process.env.SERVER_URL; +const baseUrl = process.env.APP_BASE_PATH; const constants = { apiEndpoint, - serverUrl, baseUrl, + serverUrl, 'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_create_editions', 'acl_view_editions', 'acl_loan', 'acl_loan_request', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view', 'acl_withdraw_transfer', 'acl_wallet_submit'], diff --git a/package.json b/package.json index 04894a60..9f66cfcc 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,15 @@ "node": "6.2.0" }, "scripts": { - "lint": "eslint ./js", "preinstall": "export SAUCE_CONNECT_DOWNLOAD_ON_INSTALL=true", "postinstall": "npm run build", - "build": "", - "start": "", + "build": "rimraf ./dist && NODE_ENV=production webpack -p", + "start": "NODE_ENV=production node server.js", + + "build:dev": "rimraf ./build && NODE_ENV=development webpack", + "clean": "rimraf ./build ./dist", + "start:dev": "NODE_ENV=development node server.dev.js", + "lint": "eslint ./", "test": "npm run sauce-test", "sauce-test": "mocha ./test/integration/tests/", @@ -45,12 +49,14 @@ "jest-cli": "^0.4.0", "mocha": "^2.3.4", "phantomjs2": "^2.0.2", + "rimraf": "^2.5.2", "sauce-connect-launcher": "^0.13.0", "wd": "^0.4.0" }, "dependencies": { "alt": "^0.16.5", "audiojs": "vrde/audiojs", + "autoprefixer": "^6.3.6", "babel-cli": "^6.9.0", "babel-loader": "^6.2.4", "babel-plugin-react-transform": "^2.0.2", @@ -60,19 +66,29 @@ "babel-plugin-transform-runtime": "^6.9.0", "babel-preset-es2015-no-commonjs": "0.0.2", "babel-preset-react": "^6.5.0", - "bootstrap-sass": "^3.3.4", + "babel-runtime": "^6.9.0", + "bootstrap-loader": "^1.0.10", + "bootstrap-sass": "^3.3.6", "camelcase": "^1.2.1", "classlist-polyfill": "^1.0.2", "classnames": "^1.2.2", "compression": "^1.6.2", + "core-js": "^2.4.0", + "css-loader": "^0.23.1", "decamelize": "^1.1.1", + "dotenv": "^1.2.0", "eslint": "^0.22.1", "eslint-plugin-react": "^2.5.0", + "exports-loader": "^0.6.3", "express": "^4.13.4", + "extract-text-webpack-plugin": "^1.0.1", + "file-loader": "^0.8.5", "history": "1.17.0", "invariant": "^2.1.1", "isomorphic-fetch": "^2.0.2", "moment": "^2.10.6", + "node-sass": "^3.7.0", + "postcss-loader": "^0.9.1", "q": "^1.4.1", "query-string": "^3.0.0", "raven-js": "^1.1.19", @@ -84,9 +100,16 @@ "react-star-rating": "~1.3.2", "react-textarea-autosize": "^2.5.2", "react-transform-hmr": "^1.0.4", + "resolve-url-loader": "^1.4.3", + "sass-loader": "^3.2.0", "shallow-equals": "0.0.0", "shmui": "^0.1.0", - "spark-md5": "~1.0.0" + "spark-md5": "~1.0.0", + "style-loader": "^0.13.1", + "url-loader": "^0.5.7", + "webpack": "^2.1.0-beta.7", + "webpack-combine-loaders": "^2.0.0", + "webpack-dev-server": "^2.1.0-beta.0" }, "jest": { "scriptPreprocessor": "node_modules/babel-jest", diff --git a/sass/ascribe-fonts/ascribe-fonts.scss b/sass/ascribe-fonts/ascribe-fonts.scss index 6f95a616..95aefe84 100644 --- a/sass/ascribe-fonts/ascribe-fonts.scss +++ b/sass/ascribe-fonts/ascribe-fonts.scss @@ -13,11 +13,11 @@ @font-face { font-family: 'ascribe-font'; - src:url('#{$BASE_URL}static/fonts/ascribe-font.eot?q6qoae'); - src:url('#{$BASE_URL}static/fonts/ascribe-font.eot?q6qoae#iefix') format('embedded-opentype'), - url('#{$BASE_URL}static/fonts/ascribe-font.ttf?q6qoae') format('truetype'), - url('#{$BASE_URL}static/fonts/ascribe-font.woff?q6qoae') format('woff'), - url('#{$BASE_URL}static/fonts/ascribe-font.svg?q6qoae#ascribe-font') format('svg'); + src:url('../fonts/ascribe-font.eot?q6qoae'); + src:url('../fonts/ascribe-font.eot?q6qoae#iefix') format('embedded-opentype'), + url('../fonts/ascribe-font.ttf?q6qoae') format('truetype'), + url('../fonts/ascribe-font.woff?q6qoae') format('woff'), + url('../fonts/ascribe-font.svg?q6qoae#ascribe-font') format('svg'); font-weight: normal; font-style: normal; } diff --git a/sass/bootstrap/_overrides.scss b/sass/bootstrap/_overrides.scss new file mode 100644 index 00000000..59612911 --- /dev/null +++ b/sass/bootstrap/_overrides.scss @@ -0,0 +1,2 @@ +@import '../ascribe_variables'; +@import '../variables'; diff --git a/sass/glyphicons-social.scss b/sass/glyphicons-social.scss index 96065085..3c73feb5 100644 --- a/sass/glyphicons-social.scss +++ b/sass/glyphicons-social.scss @@ -1,10 +1,10 @@ @font-face{ font-family:'Glyphicons Social'; - src:url('#{$BASE_URL}static/fonts/glyphicons-social-regular.eot'); - src:url('#{$BASE_URL}static/fonts/fonts/glyphicons-social-regular.eot?#iefix') format('embedded-opentype'), - url('#{$BASE_URL}static/fonts/glyphicons-social-regular.woff2') format('woff2'), - url('#{$BASE_URL}static/fonts/glyphicons-social-regular.woff') format('woff'), - url('#{$BASE_URL}static/fonts/glyphicons-social-regular.ttf') format('truetype'), - url('#{$BASE_URL}static/fonts/glyphicons-social-regular.svg#glyphicons_socialregular') format('svg') + src:url('../fonts/glyphicons-social-regular.eot'); + src:url('../fonts/glyphicons-social-regular.eot?#iefix') format('embedded-opentype'), + url('../fonts/glyphicons-social-regular.woff2') format('woff2'), + url('../fonts/glyphicons-social-regular.woff') format('woff'), + url('../fonts/glyphicons-social-regular.ttf') format('truetype'), + url('../fonts/glyphicons-social-regular.svg#glyphicons_socialregular') format('svg') } -.social{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Social';font-style:normal;font-weight:normal;line-height:1;vertical-align:top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.social.x05{font-size:12px}.social.x2{font-size:48px}.social.x3{font-size:72px}.social.x4{font-size:96px}.social.x5{font-size:120px}.social.light:before{color:#f2f2f2}.social.drop:before{text-shadow:-1px 1px 3px rgba(0,0,0,0.3)}.social.flip{-moz-transform:scaleX(-1);-o-transform:scaleX(-1);-webkit-transform:scaleX(-1);transform:scaleX(-1);filter:FlipH;-ms-filter:"FlipH"}.social.flipv{-moz-transform:scaleY(-1);-o-transform:scaleY(-1);-webkit-transform:scaleY(-1);transform:scaleY(-1);filter:FlipV;-ms-filter:"FlipV"}.social.rotate90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.social.rotate180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.social.rotate270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.social-pinterest:before{content:"\E001"}.social-dropbox:before{content:"\E002"}.social-google-plus:before{content:"\E003"}.social-jolicloud:before{content:"\E004"}.social-yahoo:before{content:"\E005"}.social-blogger:before{content:"\E006"}.social-picasa:before{content:"\E007"}.social-amazon:before{content:"\E008"}.social-tumblr:before{content:"\E009"}.social-wordpress:before{content:"\E010"}.social-instapaper:before{content:"\E011"}.social-evernote:before{content:"\E012"}.social-xing:before{content:"\E013"}.social-zootool:before{content:"\E014"}.social-dribbble:before{content:"\E015"}.social-deviantart:before{content:"\E016"}.social-read-it-later:before{content:"\E017"}.social-linked-in:before{content:"\E018"}.social-forrst:before{content:"\E019"}.social-pinboard:before{content:"\E020"}.social-behance:before{content:"\E021"}.social-github:before{content:"\E022"}.social-youtube:before{content:"\E023"}.social-skitch:before{content:"\E024"}.social-foursquare:before{content:"\E025"}.social-quora:before{content:"\E026"}.social-badoo:before{content:"\E027"}.social-spotify:before{content:"\E028"}.social-stumbleupon:before{content:"\E029"}.social-readability:before{content:"\E030"}.social-facebook:before{content:"\E031"}.social-twitter:before{content:"\E032"}.social-instagram:before{content:"\E033"}.social-posterous-spaces:before{content:"\E034"}.social-vimeo:before{content:"\E035"}.social-flickr:before{content:"\E036"}.social-last-fm:before{content:"\E037"}.social-rss:before{content:"\E038"}.social-skype:before{content:"\E039"}.social-e-mail:before{content:"\E040"}.social-vine:before{content:"\E041"}.social-myspace:before{content:"\E042"}.social-goodreads:before{content:"\E043"}.social-apple:before{content:"\F8FF"}.social-windows:before{content:"\E045"}.social-yelp:before{content:"\E046"}.social-playstation:before{content:"\E047"}.social-xbox:before{content:"\E048"}.social-android:before{content:"\E049"}.social-ios:before{content:"\E050"}.social-wikipedia:before{content:"\E051"}.social-pocket:before{content:"\E052"}.social-steam:before{content:"\E053"}.social-souncloud:before{content:"\E054"}.social-slideshare:before{content:"\E055"}.social-netflix:before{content:"\E056"}.social-paypal:before{content:"\E057"}.social-google-drive:before{content:"\E058"}.social-linux-foundation:before{content:"\E059"}.social-ebay:before{content:"\E060"}.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite}@-webkit-keyframes pulse{0%{-webkit-transform:scale(1)}50%{-webkit-transform:scale(1.1)}100%{-webkit-transform:scale(1)}}@keyframes pulse{0%{transform:scale(1)}50%{transform:scale(1.1)}100%{transform:scale(1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rotateIn{0%{-webkit-transform-origin:center center;-webkit-transform:rotate(-200deg);opacity:0}100%{-webkit-transform-origin:center center;-webkit-transform:rotate(0);opacity:1}}@keyframes rotateIn{0%{transform-origin:center center;transform:rotate(-200deg);opacity:0}100%{transform-origin:center center;transform:rotate(0);opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes bounce{0%,20%,50%,80%,100%{-webkit-transform:translateY(0)}40%{-webkit-transform:translateY(-30px)}60%{-webkit-transform:translateY(-15px)}}@keyframes bounce{0%,20%,50%,80%,100%{transform:translateY(0)}40%{transform:translateY(-30px)}60%{transform:translateY(-15px)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce}@-webkit-keyframes swing{20%,40%,60%,80%,100%{-webkit-transform-origin:top center}20%{-webkit-transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg)}100%{-webkit-transform:rotate(0deg)}}@keyframes swing{20%{transform:rotate(15deg)}40%{transform:rotate(-10deg)}60%{transform:rotate(5deg)}80%{transform:rotate(-5deg)}100%{transform:rotate(0deg)}}.swing{-webkit-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{0%{-webkit-transform:scale(1)}10%,20%{-webkit-transform:scale(.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale(1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale(1.1) rotate(-3deg)}100%{-webkit-transform:scale(1) rotate(0)}}@keyframes tada{0%{transform:scale(1)}10%,20%{transform:scale(.9) rotate(-3deg)}30%,50%,70%,90%{transform:scale(1.1) rotate(3deg)}40%,60%,80%{transform:scale(1.1) rotate(-3deg)}100%{transform:scale(1) rotate(0)}}.tada{-webkit-animation-name:tada;animation-name:tada} \ No newline at end of file +.social{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Social';font-style:normal;font-weight:normal;line-height:1;vertical-align:top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.social.x05{font-size:12px}.social.x2{font-size:48px}.social.x3{font-size:72px}.social.x4{font-size:96px}.social.x5{font-size:120px}.social.light:before{color:#f2f2f2}.social.drop:before{text-shadow:-1px 1px 3px rgba(0,0,0,0.3)}.social.flip{-moz-transform:scaleX(-1);-o-transform:scaleX(-1);-webkit-transform:scaleX(-1);transform:scaleX(-1);filter:FlipH;-ms-filter:"FlipH"}.social.flipv{-moz-transform:scaleY(-1);-o-transform:scaleY(-1);-webkit-transform:scaleY(-1);transform:scaleY(-1);filter:FlipV;-ms-filter:"FlipV"}.social.rotate90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.social.rotate180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.social.rotate270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.social-pinterest:before{content:"\E001"}.social-dropbox:before{content:"\E002"}.social-google-plus:before{content:"\E003"}.social-jolicloud:before{content:"\E004"}.social-yahoo:before{content:"\E005"}.social-blogger:before{content:"\E006"}.social-picasa:before{content:"\E007"}.social-amazon:before{content:"\E008"}.social-tumblr:before{content:"\E009"}.social-wordpress:before{content:"\E010"}.social-instapaper:before{content:"\E011"}.social-evernote:before{content:"\E012"}.social-xing:before{content:"\E013"}.social-zootool:before{content:"\E014"}.social-dribbble:before{content:"\E015"}.social-deviantart:before{content:"\E016"}.social-read-it-later:before{content:"\E017"}.social-linked-in:before{content:"\E018"}.social-forrst:before{content:"\E019"}.social-pinboard:before{content:"\E020"}.social-behance:before{content:"\E021"}.social-github:before{content:"\E022"}.social-youtube:before{content:"\E023"}.social-skitch:before{content:"\E024"}.social-foursquare:before{content:"\E025"}.social-quora:before{content:"\E026"}.social-badoo:before{content:"\E027"}.social-spotify:before{content:"\E028"}.social-stumbleupon:before{content:"\E029"}.social-readability:before{content:"\E030"}.social-facebook:before{content:"\E031"}.social-twitter:before{content:"\E032"}.social-instagram:before{content:"\E033"}.social-posterous-spaces:before{content:"\E034"}.social-vimeo:before{content:"\E035"}.social-flickr:before{content:"\E036"}.social-last-fm:before{content:"\E037"}.social-rss:before{content:"\E038"}.social-skype:before{content:"\E039"}.social-e-mail:before{content:"\E040"}.social-vine:before{content:"\E041"}.social-myspace:before{content:"\E042"}.social-goodreads:before{content:"\E043"}.social-apple:before{content:"\F8FF"}.social-windows:before{content:"\E045"}.social-yelp:before{content:"\E046"}.social-playstation:before{content:"\E047"}.social-xbox:before{content:"\E048"}.social-android:before{content:"\E049"}.social-ios:before{content:"\E050"}.social-wikipedia:before{content:"\E051"}.social-pocket:before{content:"\E052"}.social-steam:before{content:"\E053"}.social-souncloud:before{content:"\E054"}.social-slideshare:before{content:"\E055"}.social-netflix:before{content:"\E056"}.social-paypal:before{content:"\E057"}.social-google-drive:before{content:"\E058"}.social-linux-foundation:before{content:"\E059"}.social-ebay:before{content:"\E060"}.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite}@-webkit-keyframes pulse{0%{-webkit-transform:scale(1)}50%{-webkit-transform:scale(1.1)}100%{-webkit-transform:scale(1)}}@keyframes pulse{0%{transform:scale(1)}50%{transform:scale(1.1)}100%{transform:scale(1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rotateIn{0%{-webkit-transform-origin:center center;-webkit-transform:rotate(-200deg);opacity:0}100%{-webkit-transform-origin:center center;-webkit-transform:rotate(0);opacity:1}}@keyframes rotateIn{0%{transform-origin:center center;transform:rotate(-200deg);opacity:0}100%{transform-origin:center center;transform:rotate(0);opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes bounce{0%,20%,50%,80%,100%{-webkit-transform:translateY(0)}40%{-webkit-transform:translateY(-30px)}60%{-webkit-transform:translateY(-15px)}}@keyframes bounce{0%,20%,50%,80%,100%{transform:translateY(0)}40%{transform:translateY(-30px)}60%{transform:translateY(-15px)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce}@-webkit-keyframes swing{20%,40%,60%,80%,100%{-webkit-transform-origin:top center}20%{-webkit-transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg)}100%{-webkit-transform:rotate(0deg)}}@keyframes swing{20%{transform:rotate(15deg)}40%{transform:rotate(-10deg)}60%{transform:rotate(5deg)}80%{transform:rotate(-5deg)}100%{transform:rotate(0deg)}}.swing{-webkit-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{0%{-webkit-transform:scale(1)}10%,20%{-webkit-transform:scale(.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale(1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale(1.1) rotate(-3deg)}100%{-webkit-transform:scale(1) rotate(0)}}@keyframes tada{0%{transform:scale(1)}10%,20%{transform:scale(.9) rotate(-3deg)}30%,50%,70%,90%{transform:scale(1.1) rotate(3deg)}40%,60%,80%{transform:scale(1.1) rotate(-3deg)}100%{transform:scale(1) rotate(0)}}.tada{-webkit-animation-name:tada;animation-name:tada} diff --git a/sass/main.scss b/sass/main.scss index 9ca2a07a..03bf3cb1 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -1,12 +1,9 @@ // If you import a new .scss file, make sure to restart gulp // otherwise it will not be included -$BASE_URL: '<%= BASE_URL %>'; - @import 'mixins'; @import 'ascribe_variables'; @import 'variables'; -@import '../node_modules/bootstrap-sass/assets/stylesheets/bootstrap'; @import '../node_modules/react-star-rating/dist/css/react-star-rating.min'; @import '../node_modules/react-datepicker/dist/react-datepicker'; @import 'glyphicons-social'; diff --git a/server.dev.js b/server.dev.js new file mode 100644 index 00000000..8da3ca22 --- /dev/null +++ b/server.dev.js @@ -0,0 +1,46 @@ +/* eslint-disable strict, no-console */ +/* eslint-disable import/no-extraneous-dependencies, import/newline-after-import */ +'use strict'; + +const path = require('path'); + +const WebpackDevServer = require('webpack-dev-server'); +const webpack = require('webpack'); +const config = require('./webpack.config.js'); + +require('dotenv').load({ silent: true }); + +const HOST = process.env.ONION_DEV_HOST || 'localhost'; +const PORT = process.env.ONION_DEV_PORT || 3000; + +// Enable server hot reloading +// React hot reloading is enabled through .babelrc and babel-react-transform +config.entry.unshift(`webpack-dev-server/client?http://${HOST}:${PORT}/`, + 'webpack/hot/dev-server'); +config.plugins.push(new webpack.HotModuleReplacementPlugin()); + +// Specify output location for bundled files +config.output.publicPath = '/static/'; + +// Configure server +const compiler = webpack(config); + +const server = new WebpackDevServer(compiler, { + publicPath: config.output.publicPath, + contentBase: './build', + historyApiFallback: { + index: config.output.publicPath + }, + hot: true, + stats: { colors: true } +}); + +// Start server +server.listen(PORT, HOST, (err) => { + if (err) { + console.error(`Onion dev server ran into ${err} while starting on ${HOST}:${PORT} ` + + `with basePath set to ${BASE_PATH || '/'}. Shutting down...`); + server.close(); + } + console.log(`Onion server running on ${HOST}:${PORT} with basePath set to ${BASE_PATH || '/'}.`); +}); diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..e202db69 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,163 @@ +/* eslint-disable strict, no-console, object-shorthand */ +/* eslint-disable import/no-extraneous-dependencies, import/newline-after-import */ +'use strict'; + +const path = require('path'); + +const webpack = require('webpack'); +const autoPrefixer = require('autoprefixer'); +const combineLoaders = require('webpack-combine-loaders'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); + +require('dotenv').load({ silent: true }); + +const PRODUCTION = process.env.NODE_ENV === 'production'; + +const PATHS = { + APP: path.resolve(__dirname, 'js/app.js'), + BUILD: path.resolve(__dirname, 'build'), + DIST: path.resolve(__dirname, 'dist'), + NODE_MODULES: path.resolve(__dirname, 'node_modules'), +}; + + +/** EXTERNAL DEFINITIONS INJECTED INTO APP **/ +const DEFINITIONS = { + 'process.env': { + NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development'), +}; + + +/** PLUGINS **/ +const PLUGINS = [ + new webpack.DefinePlugin(DEFINITIONS), + new webpack.NoErrorsPlugin(), + // Extract stylesheets out of bundle + new ExtractTextPlugin( + PRODUCTION ? 'css/styles.min.css' : 'css/styles.css', { + allChunks: true + } + ), +const PROD_PLUGINS = [ + new webpack.optimize.UglifyJsPlugin({ + compress: { + warnings: false + }, + output: { + comments: false + } + }), + new webpack.LoaderOptionsPlugin({ + debug: false, + minimize: true + }), +]; + +if (PRODUCTION) { + PLUGINS.push(...PROD_PLUGINS); +} + + +/** LOADERS **/ +const JS_LOADER = combineLoaders([ + { + loader: 'babel', + query: { + cacheDirectory: true, + }, + }, +]); + +const CSS_LOADER = combineLoaders([ + { + loader: 'css', + query: { + sourceMap: true, + }, + }, + { loader: 'postcss' }, +]); + +const SASS_LOADER = `${CSS_LOADER}!` + combineLoaders([ + { + loader: 'sass', + query: { + precision: '8', // See https://github.com/twbs/bootstrap-sass#sass-number-precision + outputStyle: 'expanded', + sourceMap: true, + }, + } +]); + +const URL_FONT_LOADER = combineLoaders([ + { + loader: 'url', + query: { + limit: 10000, + name: 'fonts/[name].[ext]', + }, + }, +]); + +const FILE_FONT_LOADER = combineLoaders([ + { + loader: 'file', + query: { + name: 'fonts/[name].[ext]', + }, + }, +]); + +const LOADERS = [ + { + test: /\.jsx?$/, + exclude: [PATHS.NODE_MODULES], + loader: JS_LOADER, + }, + { + test: /\.s[ac]ss$/, + exclude: [PATHS.NODE_MODULES], + loader: ExtractTextPlugin.extract('style', SASS_LOADER), + }, + + // Fonts + // woffs and svgs are typically smaller should we can try to load them as a url + { + test: /\.(woff2?|svg)$/, + loader: URL_FONT_LOADER, + }, + { + test: /\.(ttf|eot)$/, + loader: FILE_FONT_LOADER, + }, +]; + + +/** EXPORTED WEBPACK CONFIG **/ +const config = { + entry: ['bootstrap-loader/extractStyles', PATHS.APP], + + output: { + filename: PRODUCTION ? 'js/bundle.min.js' : 'js/bundle.js', + path: PRODUCTION ? PATHS.DIST : PATHS.BUILD, + }, + + debug: !PRODUCTION, + + devtool: PRODUCTION ? '#source-map' : '#inline-source-map', + + resolve: { + extensions: ['', '.js', '.jsx'], + modules: ['node_modules'], // Don't use absolute path here to allow recursive matching + }, + + plugins: PLUGINS, + + module: { + loaders: LOADERS, + }, + + postcss: [autoPrefixer()], +}; + +module.exports = config;