Initial commit

This commit is contained in:
James Barnsley
2016-10-03 14:44:27 +13:00
commit a414888b2a
28 changed files with 1219 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/node_modules/
/production/
*.orig
Thumbs.db

35
package.json Executable file
View File

@ -0,0 +1,35 @@
{
"name": "test-reactor",
"version": "1.0.0",
"description": "",
"main": "app.js",
"dependencies": {
"babel": "^5.8.29",
"babel-core": "6.14.0",
"babel-loader": "6.2.5",
"babel-preset-es2015": "*",
"babel-preset-react": "*",
"flux": "^2.1.1",
"redux": "*",
"react": "*",
"react-dom": "*",
"react-router": "*",
"redux": "*",
"react-redux": "*",
"redux-devtools": "*",
"webpack": "^1.13.2",
"webpack-strip": "*",
"node-sass": "*",
"extract-text-webpack-plugin": "*",
"jquery": "3.1.1",
"mopidy": "^0.5.0",
"expose-loader": "*",
"kioc": "1.0.4"
},
"scripts": {
"dev": "cp src/index.html production/index.html & NODE_ENV=development webpack",
"prod": "NODE_ENV=production webpack"
},
"author": "James Barnsley",
"license": "ISC"
}

81
src/App.js Executable file
View File

@ -0,0 +1,81 @@
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { createStore, bindActionCreators } from 'redux'
import { Link } from 'react-router'
import { connect } from 'react-redux'
import * as actions from './actions'
import Services from './services/Services'
/**
* The application 'brain'
*
* All data handling and fetching is done through this handler
**/
class App extends React.Component{
constructor(props){
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
console.log('click');
//this.props.providerActions.authorizeSpotify();
Services.get('services.mopidy')
.then( function(MopidyService){
MopidyService.connection.playback.getState()
.then( function(state){
console.log('App.js > playback state', state);
});
})
}
componentDidMount(){
Services.get('services.mopidy')
.then( function(MopidyService){
MopidyService.connection.playback.getState()
.then( function(state){
console.log('App.js > playback state', state);
});
})
}
render(){
return (
<div>
<ul role="nav">
<li><Link to="/now-playing">Now playing</Link></li>
<li><Link to="/album/6N51k5TP5pSZYPf7bLffLe">808's and Heartbreak</Link></li>
<li><Link to="/album/1PgfRdl3lPyACfUGH4pquG">A million</Link></li>
<li><Link to="/library/albums">Library: Albums</Link></li>
<li><Link to="/album">Album</Link></li>
<li><a onClick={this.handleClick}>Authorize</a></li>
</ul>
{this.props.children}
</div>
);
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)

8
src/about/index.js Executable file
View File

@ -0,0 +1,8 @@
import React from 'react'
export default React.createClass({
render() {
return <div>About</div>
}
})

32
src/actions.js Executable file
View File

@ -0,0 +1,32 @@
/**
* Actions and Action Creators
**/
export const TOGGLE_DONE = 'TOGGLE_DONE'
export const ADD_TODO = 'ADD_TODO'
export const SET_ALBUM = 'SET_ALBUM'
export function toggleDone( id ){
return {
type: TOGGLE_DONE,
id: id
}
}
export function addTodo( title ){
return {
type: ADD_TODO,
id: title,
title: title
}
}
export function setAlbum( album ){
return {
type: SET_ALBUM,
album: album
}
}

84
src/common/Album.js Executable file
View File

@ -0,0 +1,84 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import Tracklist from '../common/Tracklist'
import * as actions from './actions'
class Album extends React.Component{
constructor(props) {
super(props);
this.state = {
album: false
}
}
// on render
componentDidMount(){
this.loadAlbum( this.props.params.id );
}
// when props changed
componentWillReceiveProps( nextProps ){
if( nextProps.params.id != this.props.params.id ){
this.loadAlbum( nextProps.params.id );
}
}
loadAlbum( id ){
let self = this;
$.ajax({
method: 'GET',
cache: true,
url: 'https://api.spotify.com/v1/albums/'+id,
success: function(album){
self.setState({ album: album });
}
});
}
renderAlbum(){
if( this.state.album ){
return (
<div>
<h3>{ this.state.album.name }</h3>
<h3>{ this.state.album.label }</h3>
<Tracklist tracks={this.state.album.tracks.items} />
</div>
);
}
return null;
}
render(){
return (
<div>
<h3>Single album</h3>
{ this.renderAlbum() }
</div>
);
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Album)

42
src/common/Tracklist.js Executable file
View File

@ -0,0 +1,42 @@
import React, { PropTypes } from 'react'
export default class Tracklist extends React.Component{
constructor(props) {
super(props);
}
flattenTracks(){
var tracks = [];
if( this.props.tracks ){
var originalTracks = this.props.tracks;
for( var i = 0; i < originalTracks.length; i++ ){
var track = originalTracks[i];
if( typeof(track.track) !== 'undefined' ){
track.track.tlid = track.tlid;
track = track.track;
}
tracks.push( track );
}
}
return tracks;
}
render(){
if( this.flattenTracks ){
return (
<ul>
{
this.flattenTracks().map( track =>
<li key={track.uri}>{ track.name }</li>
)
}
</ul>
);
}
return null;
}
}

12
src/common/actions.js Executable file
View File

@ -0,0 +1,12 @@
/**
* Actions and Action Creators
**/
export const LOAD_ALBUMS = 'LOAD_ALBUMS'
export function loadAlbums(){
return {
type: LOAD_ALBUMS
}
}

16
src/common/reducer.js Executable file
View File

@ -0,0 +1,16 @@
import * as actions from './actions'
export default function album(album = {}, action) {
switch (action.type) {
case actions.LOAD_ALBUM:
return album//Object.assign({}, album, action.album);
default:
return album
}
}

37
src/config/Mopidy.js Executable file
View File

@ -0,0 +1,37 @@
import React, { PropTypes } from 'react'
import * as actions from './actions'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
class Mopidy extends React.Component{
constructor(props){
super(props);
let self = this;
console.log('Mopidy > constructor')
}
render(){
return null
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Mopidy)

79
src/config/Spotify.js Executable file
View File

@ -0,0 +1,79 @@
import React, { PropTypes } from 'react'
import * as actions from './actions'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
class Spotify extends React.Component{
constructor(props){
super(props);
let self = this;
this.urlBase = 'https://api.spotify.com/v1/';
console.log('SpotifyProvider > constructor')
// listen for incoming messages from the authorization iframe
// this is triggered when authentication is granted from the popup
window.addEventListener('message', function(event){
if( !/^https?:\/\/jamesbarnsley\.co\.nz/.test(event.origin) ) return false;
self.handleMessage( event );
}, false);
}
handleMessage( event ){
// convert to json
var data = JSON.parse(event.data);
// fire event
this.props.actions.authorizeSpotifySuccess( data );
}
test(){
console.log('test stuff');
}
getAlbum( albumid ){
return $.when(
$.ajax({
method: 'GET',
cache: true,
url: this.urlBase+'albums/'+albumid
})
);
}
render(){
if( this.props.config.authorizingSpotify ){
var src = '//jamesbarnsley.co.nz/spotmop.php?action=authorize&app='+location.protocol+'//'+window.location.host;
return (
<div>
Authorizing...
<iframe id="authorization-frame" src={src}></iframe>
</div>
);
}
return null
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Spotify)

21
src/config/actions.js Executable file
View File

@ -0,0 +1,21 @@
/**
* Actions and Action Creators
**/
export const AUTHORIZE_SPOTIFY = 'AUTHORIZE_SPOTIFY'
export const AUTHORIZE_SPOTIFY_SUCCESS = 'AUTHORIZE_SPOTIFY_SUCCESS'
export function authorizeSpotify( authorize = true ){
return {
type: AUTHORIZE_SPOTIFY,
authorize: authorize
}
}
export function authorizeSpotifySuccess( authorization ){
return {
type: AUTHORIZE_SPOTIFY_SUCCESS,
authorization: authorization
}
}

24
src/config/reducer.js Executable file
View File

@ -0,0 +1,24 @@
import * as actions from './actions'
export default function config(config = {}, action){
switch (action.type) {
case actions.AUTHORIZE_SPOTIFY:
return Object.assign({}, config, {
authorizingSpotify: action.authorize
});
case actions.AUTHORIZE_SPOTIFY_SUCCESS:
console.info('Spotify authorization successful');
var config = Object.assign({}, config, action.authorization)
config = Object.assign({}, config, { authorizingSpotify: false })
return config
default:
return config
}
}

20
src/index.html Executable file
View File

@ -0,0 +1,20 @@
<html>
<head>
<title>Testing React and Redux</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta base-href="/alto/production" />
</head>
<body>
<div id="app">
<!-- ReactJS application here -->
</div>
</body>
<script type="text/javascript" src="app.js"></script>
</html>

54
src/index.js Executable file
View File

@ -0,0 +1,54 @@
/**
* Base-level application wrapper
**/
console.info('Application initiated');
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import { Router, Route, Link, hashHistory } from 'react-router'
import reducer from './reducer'
import App from './App'
import LibraryAlbums from './library/LibraryAlbums'
import Album from './common/Album'
import NowPlaying from './routes/NowPlaying'
import Services from './services/Services'
Promise.all(Services.setup())
.then(() => {
console.info('Services started...');
});
/**
* Render our application into the real DOM
*
* Sets up all of our routes, and the relevant components for each.
* Provider facilitates global access to the store by using connect()
<Route path="playlists" component={LibraryPlaylists} />
<Route path="albums" component={LibraryAlbums} />
<Route path="artists" component={LibraryArtists} />
<Route path="tracks" component={LibraryTracks} />
**/
ReactDOM.render(
<Provider store={ createStore( reducer, { todos: [] } ) }>
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="album/:id" component={Album} />
<Route path="library/albums" component={LibraryAlbums} />
<Route path="now-playing" component={NowPlaying} />
</Route>
</Router>
</Provider>,
document.getElementById('app')
);

68
src/library/LibraryAlbums.js Executable file
View File

@ -0,0 +1,68 @@
import React, { PropTypes } from 'react'
import * as actions from './actions'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
class LibraryAlbums extends React.Component{
constructor(props) {
super(props);
this.state = {
albums: []
};
}
// on load of this component
componentDidMount(){
var url = 'http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=4320a3ef51c9b3d69de552ac083c55e3&artist=Cher&album=Believe&format=json';
//var url = 'https://pixabay.com/api/?key=3190651-5f431f6c829ace6d75ff07701&q=yellow+flowers&image_type=photo&pretty=true';
var self = this;
// url (required), options (optional)
fetch(url, {
method: 'get'
}).then(function(response) {
return response.json();
}).then(function( json ) {
self.setState({ albums: json.hits });
}).catch(function(err) {
console.error( err );
});
}
render() {
return (
<div>
<h3>Library album</h3>
{
this.state.albums.map(album =>
<h4 key={album.webformatURL}>{ album.webformatURL }</h4>
)
}
</div>
);
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LibraryAlbums)

12
src/library/actions.js Executable file
View File

@ -0,0 +1,12 @@
/**
* Actions and Action Creators
**/
export const LOAD_ALBUMS = 'LOAD_ALBUMS'
export function loadAlbums(){
return {
type: LOAD_ALBUMS
}
}

16
src/library/reducer.js Executable file
View File

@ -0,0 +1,16 @@
import * as actions from './actions'
export default function album(album = {}, action) {
switch (action.type) {
case actions.SET_ALBUM:
return Object.assign({}, album, action.album);
default:
return album
}
}

21
src/reducer.js Executable file
View File

@ -0,0 +1,21 @@
/**
* Root reducer
*
* This combines all our application reducers into a single, top-level reducer.
* We need to combine reducers to create the unified application-level store.
**/
// import all of our application reducers
import todos from './todos/reducer'
import config from './config/reducer'
import { combineReducers } from 'redux'
// combine them into one root reducer
export default combineReducers({
todos,
config
})

67
src/routes/NowPlaying.js Executable file
View File

@ -0,0 +1,67 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import Services from '../services/Services'
import Tracklist from '../common/Tracklist'
import * as actions from '../actions'
class NowPlaying extends React.Component{
constructor(props) {
super(props);
this.state = {
tracks: []
}
}
// on render
componentDidMount(){
var self = this;
Services.get('services.mopidy')
.then( function(MopidyService){
MopidyService.connection.tracklist.getTlTracks()
.then( function(tracks){
self.setState({ tracks : tracks });
});
})
}
renderTracks(){
if( this.state.tracks ){
return (
<Tracklist tracks={this.state.tracks} />
);
}
return null;
}
render(){
return (
<div>
<h3>Now playing</h3>
{ this.renderTracks() }
</div>
);
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NowPlaying)

35
src/services/MopidyService.js Executable file
View File

@ -0,0 +1,35 @@
import Mopidy from 'mopidy'
function MopidyService( connection ){
this.connection = connection;
};
MopidyService.attachKey = 'services.mopidy';
MopidyService.attach = function( app ){
console.info('MopidyService: Attaching...');
return new Promise((resolve) => {
var mopidyhost = 'music.plasticstudio.co';//window.location.hostname;
var mopidyport = "6680";
var protocol = 'ws';
var connection = new Mopidy({
webSocketUrl: protocol+"://" + mopidyhost + ":" + mopidyport + "/mopidy/ws",
callingConvention: 'by-position-or-by-name'
});
connection.on((a, msg) => {
//console.log(msg);
// msg && msg.method ?
// console.log('<-', msg.method) :
// (msg ? console.log('->', JSON.parse(msg.data)) : console.log('->', msg));
});
connection.on('state:online', () => {
console.info('MopidyService: Attached');
var service = new MopidyService( connection );
resolve( service );
});
});
}
export default MopidyService;

11
src/services/Services.js Executable file
View File

@ -0,0 +1,11 @@
import ServicesContainer from 'kioc';
import MopidyService from './MopidyService';
import SpotifyService from './SpotifyService';
var Services = new ServicesContainer();
Services.use(MopidyService, true);
//Services.use(SpotifyService, true);
export default Services;

79
src/services/SpotifyService.js Executable file
View File

@ -0,0 +1,79 @@
import React, { PropTypes } from 'react'
import * as actions from './actions'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
class SpotifyService extends React.Component{
constructor(props){
super(props);
let self = this;
this.urlBase = 'https://api.spotify.com/v1/';
console.log('SpotifyProvider > constructor')
// listen for incoming messages from the authorization iframe
// this is triggered when authentication is granted from the popup
window.addEventListener('message', function(event){
if( !/^https?:\/\/jamesbarnsley\.co\.nz/.test(event.origin) ) return false;
self.handleMessage( event );
}, false);
}
handleMessage( event ){
// convert to json
var data = JSON.parse(event.data);
// fire event
this.props.actions.authorizeSpotifySuccess( data );
}
test(){
console.log('test stuff');
}
getAlbum( albumid ){
return $.when(
$.ajax({
method: 'GET',
cache: true,
url: this.urlBase+'albums/'+albumid
})
);
}
render(){
if( this.props.config.authorizingSpotify ){
var src = '//jamesbarnsley.co.nz/spotmop.php?action=authorize&app='+location.protocol+'//'+window.location.host;
return (
<div>
Authorizing...
<iframe id="authorization-frame" src={src}></iframe>
</div>
);
}
return null
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SpotifyService)

29
src/services/actions.js Executable file
View File

@ -0,0 +1,29 @@
/**
* Actions and Action Creators
**/
export const AUTHORIZE_SPOTIFY = 'AUTHORIZE_SPOTIFY'
export const AUTHORIZE_SPOTIFY_SUCCESS = 'AUTHORIZE_SPOTIFY_SUCCESS'
export const MOPIDY_ONLINE = 'MOPIDY_ONLINE'
export function authorizeSpotify( authorize = true ){
return {
type: AUTHORIZE_SPOTIFY,
authorize: authorize
}
}
export function authorizeSpotifySuccess( authorization ){
return {
type: AUTHORIZE_SPOTIFY_SUCCESS,
authorization: authorization
}
}
export function mopidyOnline( online ){
return {
type: MOPIDY_ONLINE,
online: online
}
}

29
src/services/reducer.js Executable file
View File

@ -0,0 +1,29 @@
import * as actions from './actions'
export default function reducer(state = {}, action){
switch (action.type) {
case actions.AUTHORIZE_SPOTIFY:
return Object.assign({}, state.spotify, {
authorizing: action.authorize
});
case actions.AUTHORIZE_SPOTIFY_SUCCESS:
console.info('Spotify authorization successful');
var state = Object.assign({}, state.spotify, action.authorization)
state = Object.assign({}, state.spotify, { online: true, authorizingSpotify: false })
return state
case actions.MOPIDY_ONLINE:
return Object.assign({}, state.mopidy, {
online: action.online
});
default:
return state
}
}

158
src/todos/index.js Executable file
View File

@ -0,0 +1,158 @@
import React, { PropTypes } from 'react'
import * as actions from '../actions'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
class Todos extends React.Component{
constructor(props) {
super(props);
}
// on load of this component
componentDidMount(){
this.props.actions.load();
}
render(){
return (
<Todos_View todos={this.props.todos} actions={this.props.actions} />
);
}
}
/**
* The Todo's presenter, or view
*
* Any UI and UX display occurs here, and is passed back to it's brain through props
* This allows for reuse of the brain for different contexts without duplicating functionality
**/
class Todos_View extends React.Component{
constructor(props) {
super(props);
}
render(){
return (
<div>
<ul className="todos">
{
this.props.todos.map(todo =>
<TodoItem todo={todo} key={todo.id} actions={this.props.actions} />
)
}
</ul>
<AddTodoForm actions={this.props.actions} />
</div>
);
}
}
/**
* The Todo item
**/
class TodoItem extends React.Component{
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.props.actions.toggleDone(this.props.todo.id);
this.props.actions.save();
}
render(){
var style = {};
if( this.props.todo.completed ) style.textDecoration = 'line-through';
return <li onClick={this.handleClick} style={style}>{ this.props.todo.title }</li>;
}
}
/**
* The Todo item
**/
class AddTodoForm extends React.Component{
constructor(props) {
super(props);
this.state = {
title: ''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(){
this.props.actions.addTodo( this.state.title );
this.props.actions.save();
this.setState({ title: '' });
}
handleChange( newTitle ){
this.setState({ title: newTitle });
}
render(){
return <AddTodoForm_View title={this.state.title} handleChange={this.handleChange} handleSubmit={this.handleSubmit} />
}
}
class AddTodoForm_View extends React.Component{
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange( event ){
this.props.handleChange( event.target.value );
}
handleSubmit( event ){
event.preventDefault();
this.props.handleSubmit();
}
render(){
return (
<form onSubmit={ this.handleSubmit }>
<input type="text" value={ this.props.title } onChange={this.handleChange} />
<input type="submit" value="Add" />
</form>
);
}
}
/*
Todos.propTypes = {
todos: React.PropTypes.array.isRequired
};
*/
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Todos)

38
src/todos/reducer.js Executable file
View File

@ -0,0 +1,38 @@
import * as actions from '../actions'
export default function todos(todos = [], action){
switch( action.type ){
case actions.LOAD:
if( localStorage.getItem( 'todos' ) ){
return JSON.parse( localStorage.getItem( 'todos' ) );
}
return [];
case actions.SAVE:
localStorage.setItem( 'todos', JSON.stringify(todos) );
return todos;
case actions.ADD_TODO:
return Object.assign([], todos, [...todos, { id: action.id, title: action.title, completed: false }] )
case actions.TOGGLE_DONE:
return Object.assign([], todos,
todos.map(todo => {
if( todo.id === action.id ){
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
);
default:
return todos
}
}

107
webpack.config.js Executable file
View File

@ -0,0 +1,107 @@
var dev = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var node_dir = __dirname + '/node_modules';
var output_dir = __dirname +"/production"
var config = {
context: __dirname,
entry: "./src/index.js",
output: {
path: output_dir,
filename: 'app.js'
},
module: {
loaders: [
{ test: require.resolve('jquery'), loader: 'expose?jQuery!expose?$' },
{
// loading JSX (aka Babel) into browser-friendly ES6
test: /\.js$/,
exclude: [
/(node_modules|bower_components)/,
'index.js',
],
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
},
{
// loading sass asset files
test: /\.scss$/,
loader: ExtractTextPlugin.extract([
'css'+(dev? '?sourceMap=true': ''),
'sass'+(dev? '?outputStyle=expanded&sourceMap=true&sourceMapContents=true': '')
])
}
]
},
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
]
};
/**
* Development-only configuration values
**/
if( dev ){
// set compiled css location
config.plugins.push( new ExtractTextPlugin("app.css") );
// we want source maps
config.devtool = 'source-map';
/**
* Production-only configuration values
**/
}else{
// set our final output filename
config.output.filename = 'app.min.js';
// re-iterate our production value as a string (for ReactJS building)
config.plugins.push(
new webpack.DefinePlugin({
'process.env':{
'NODE_ENV': JSON.stringify('production')
}
})
);
// remove all debug and console code
config.module.loaders.push(
{
test: /\.(js|jsx)$/,
loader: "webpack-strip?strip[]=console.log,strip[]=console.info,strip[]=debug"
}
);
// set compiled css location
config.plugins.push( new ExtractTextPlugin("app.min.css") );
// uglify our js, with no sourcemaps
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: true,
mangle: false,
sourceMap: false,
comments: false
})
);
}
// now export our collated config object
module.exports = config;