├── .meteor ├── .finished-upgraders ├── .gitignore ├── .id ├── cordova-plugins ├── packages ├── platforms ├── release └── versions ├── README.md ├── client ├── action_creators.jsx ├── lib │ ├── app.browserify.js │ └── app.browserify.options.json ├── main.jsx ├── middlewares.jsx ├── reducers.jsx └── store.jsx ├── leaderboard.css ├── leaderboard.html ├── leaderboard.js ├── packages.json └── packages └── npm-container ├── .npm └── package │ ├── .gitignore │ ├── README │ └── npm-shrinkwrap.json ├── index.js └── package.js /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | notices-for-0.9.0 8 | notices-for-0.9.1 9 | 0.9.4-platform-file 10 | notices-for-facebook-graph-api-2 11 | notices-for-0.9.0 12 | notices-for-0.9.1 13 | 0.9.4-platform-file 14 | notices-for-facebook-graph-api-2 15 | -------------------------------------------------------------------------------- /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | f98bo41q74rpv12upjp8 8 | -------------------------------------------------------------------------------- /.meteor/cordova-plugins: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | insecure 8 | autopublish 9 | meteor-platform 10 | meteorhacks:npm 11 | npm-container 12 | cosmos:browserify 13 | skinnygeek1010:flux-helpers 14 | reactive-dict 15 | react 16 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | android 4 | ios 5 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.1.0.2 2 | -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | autopublish@1.0.3 2 | autoupdate@1.2.1 3 | babel-compiler@5.4.7 4 | babel-runtime@0.1.0 5 | base64@1.0.3 6 | binary-heap@1.0.3 7 | blaze@2.1.2 8 | blaze-tools@1.0.3 9 | boilerplate-generator@1.0.3 10 | callback-hook@1.0.3 11 | check@1.0.5 12 | coffeescript@1.0.6 13 | cosmos:browserify@0.4.0 14 | ddp@1.1.0 15 | deps@1.0.7 16 | ejson@1.0.6 17 | fastclick@1.0.3 18 | geojson-utils@1.0.3 19 | html-tools@1.0.4 20 | htmljs@1.0.4 21 | http@1.1.0 22 | id-map@1.0.3 23 | insecure@1.0.3 24 | jquery@1.11.3_2 25 | json@1.0.3 26 | jsx@0.1.0 27 | launch-screen@1.0.2 28 | livedata@1.0.13 29 | logging@1.0.7 30 | meteor@1.1.6 31 | meteor-platform@1.2.2 32 | meteorhacks:async@1.0.0 33 | meteorhacks:npm@1.3.0 34 | minifiers@1.1.5 35 | minimongo@1.0.8 36 | mobile-status-bar@1.0.3 37 | mongo@1.1.0 38 | npm-container@1.0.0 39 | observe-sequence@1.0.6 40 | ordered-dict@1.0.3 41 | random@1.0.3 42 | react@0.1.1 43 | react-meteor-data@0.1.5 44 | react-runtime@0.13.3_6 45 | react-runtime-dev@0.13.3_5 46 | react-runtime-prod@0.13.3_4 47 | reactive-dict@1.1.0 48 | reactive-var@1.0.5 49 | reload@1.1.3 50 | retry@1.0.3 51 | routepolicy@1.0.5 52 | session@1.1.0 53 | skinnygeek1010:flux-helpers@0.1.0 54 | spacebars@1.0.6 55 | spacebars-compiler@1.0.6 56 | templating@1.1.1 57 | tracker@1.0.7 58 | ui@1.0.6 59 | underscore@1.0.3 60 | url@1.0.4 61 | webapp@1.2.0 62 | webapp-hashing@1.0.3 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Meteor Redux 2 | 3 | Please read the fantastic Redux guide before/after diving into this. At first it may seem very complex 4 | but it turns out to be very simple once you understand the reducer flow. 5 | 6 | **[Redux Guide](http://rackt.github.io/redux/index.html)** 7 | 8 | Basic gist (from Redux guide): 9 | 10 | Meteor apps need to store local app state in an organized way. Using Session works but can lead to 11 | maintainance problems down the road. Redux helps keep things organized and de-coupled. 12 | 13 | ```javascript 14 | // global reactive store is setup on app startup 15 | store = createStoreWithMiddleware(appReducer); 16 | 17 | // store has initial empty values: 18 | // { 19 | // selectedPlayerId: '', 20 | // selectedPlayerName: '', 21 | // foo: 1, 22 | // bar: 2, 23 | // } 24 | 25 | 26 | // view calls action and returns into store dispatcher 27 | // 28 | Template.player.events({ 29 | 'click': function () { 30 | store.dispatch( Actions.selectPlayer(this._id) ); 31 | } 32 | }); 33 | 34 | 35 | // action 'creator' is a normal function that 36 | // just creates an action object and returns it 37 | // 38 | Actions.selectPlayer = function selectPlayer(playerId) { 39 | let player = Players.findOne(playerId); 40 | let playerName = player.name || "N/A"; 41 | 42 | return { 43 | type: 'SELECT_PLAYER', 44 | playerId: playerId, 45 | playerName: playerName 46 | }; 47 | }; 48 | 49 | 50 | // app reducer catches action in switch statement and can mutate the 51 | // data based on action payload metadata. reactState is a reactive-dict 52 | // so we just set the final value and return the dict when done. 53 | // 54 | // if the app was large we could have several reducers that combine the 55 | // state into the final root object. Redux can only have 1 store 56 | // 57 | appReducer = function appReducer(state, action) { 58 | state = state || reacState; 59 | // see action_creators.jsx for action payloads 60 | 61 | switch (action.type) { 62 | case 'SELECT_PLAYER': 63 | // we're setting the reactive dict here 64 | state.set('selectedPlayerId', action.playerId); 65 | state.set('selectedPlayerName', action.playerName); 66 | // then returning the entire dict when done 67 | return state; 68 | case 'INCREMENT_SCORE': 69 | // collections are in Minimongo but you could also keep 70 | // them here if its easier to have all data in one place 71 | // see React repo for example of that 72 | return state; 73 | default: 74 | return state; 75 | } 76 | } 77 | 78 | 79 | // templates can consume this global state with the `store` helper 80 | // but can only mutate it by calling an action 81 | // 82 | 89 | 90 | ``` 91 | 92 | 93 | 94 | ### Usage 95 | 96 | - `meteor` 97 | - Open your browser to localhost:3000 98 | - Checkout action/store logs in console after clicking about 99 | -------------------------------------------------------------------------------- /client/action_creators.jsx: -------------------------------------------------------------------------------- 1 | // action creators are functions that take a param, build up an 'action' 2 | // and return it to the consumer (reducer). This may seem like 3 | // unneeded boilerplate but it's **really** nice to have a file 4 | // with *all* possible ways to mutate the state of the app. 5 | 6 | Actions = {}; 7 | 8 | // doesn't return payload because the reactive store state will re-render views 9 | Actions.incrementScore = function incrementScore(playerId) { 10 | Players.update({_id: playerId}, {$inc: {score: 5}}); 11 | // TODO call FAILED action on error 12 | return { type: 'INCREMENT_SCORE' }; 13 | }; 14 | 15 | 16 | Actions.selectPlayer = function selectPlayer(playerId) { 17 | let player = Players.findOne(playerId); 18 | let playerName = player.name || "N/A"; 19 | 20 | return { 21 | type: 'SELECT_PLAYER', 22 | playerId: playerId, 23 | playerName: playerName 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /client/lib/app.browserify.js: -------------------------------------------------------------------------------- 1 | Redux = require("redux"); 2 | // ReactRedux = require("react-redux"); 3 | ReduxDevTools = require("redux-devtools"); 4 | ReactReduxDevTools = require("redux-devtools/lib/react"); 5 | DevTools = ReactReduxDevTools.DevTools; 6 | DebugPanel = ReactReduxDevTools.DebugPanel; 7 | LogMonitor = ReactReduxDevTools.LogMonitor; 8 | -------------------------------------------------------------------------------- /client/lib/app.browserify.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "transforms": { 3 | "externalify": { 4 | "global": true, 5 | "external": { 6 | "react": "React.require" 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/main.jsx: -------------------------------------------------------------------------------- 1 | Meteor.startup(function() { 2 | React.render( 3 |
4 | 5 | 6 | 7 |
, 8 | document.getElementById('react-root')); 9 | }); 10 | -------------------------------------------------------------------------------- /client/middlewares.jsx: -------------------------------------------------------------------------------- 1 | // middleware allows you to do something in between the dispatch 2 | // and handing it off to the reducer 3 | 4 | // console.log our state changes 5 | logger = store => next => action => { 6 | log('\n[Dispatching]', action); 7 | // essentially call 'dispatch' 8 | let result = next(action); 9 | log('[Store]', store.getState().keys); 10 | return result; 11 | }; 12 | 13 | function log() { 14 | if (__debug_redux) { 15 | console.log.apply(console, arguments); 16 | } 17 | } 18 | __debug_redux = true 19 | -------------------------------------------------------------------------------- /client/reducers.jsx: -------------------------------------------------------------------------------- 1 | // reducers allow you to 'slice' off a part of the single state object which 2 | // lets you think about the domain in a smaller picture. You could use one 3 | // reducer in a small app like this but in large apps this reducer could be 4 | // several hundred lines. See the meteor-flux-leaderboard redux branch for an 5 | // example of this. 6 | 7 | let { incrementScore, selectPlayer, playersChanged } = Actions; 8 | 9 | 10 | // we'll use a reactive dict as the root state object 11 | // so that our views can auto-render on change 12 | var reactiveState = new ReactiveDict('redux-state'); 13 | 14 | 15 | appReducer = function appReducer(state, action) { 16 | state = state || reactiveState; 17 | // see action_creators.jsx for action payloads 18 | 19 | switch (action.type) { 20 | case 'SELECT_PLAYER': 21 | state.set('selectedPlayerId', action.playerId); 22 | state.set('selectedPlayerName', action.playerName); 23 | return state; 24 | case 'INCREMENT_SCORE': 25 | return state; 26 | default: 27 | return state; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/store.jsx: -------------------------------------------------------------------------------- 1 | const { createStore, combineReducers, applyMiddleware } = Redux; 2 | const { devTools, persistState } = ReduxDevTools; 3 | 4 | // Redux has a single store. to reduce complexity it allows you to combine 5 | // several 'reducer' functions that share this single state object. 6 | // They are combined into one root reducer which is passed to the store, 7 | // however for this app we only have one reducer. 8 | 9 | 10 | // applyMiddleware takes createStore() and returns a new wrapped createStore 11 | // note, this is an optional step to use middleware (we're auto console.log dispatches) 12 | // let createStoreWithMiddleware = applyMiddleware(logger)(createStore); 13 | 14 | const createStoreWithMiddleware = 15 | applyMiddleware(logger)( 16 | devTools()( 17 | persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))( 18 | createStore 19 | ) 20 | ) 21 | ); 22 | 23 | store = createStoreWithMiddleware(appReducer); 24 | 25 | 26 | 27 | // add our own helper for reactive-dicts, you could 28 | // also just call getState 29 | store.getReactiveState = function(key) { 30 | return store.getState().get(key); 31 | }; 32 | 33 | // add a global helper for Blaze 34 | UI.registerHelper('store', function(key) { 35 | return store.getState().get(key); 36 | }); 37 | -------------------------------------------------------------------------------- /leaderboard.css: -------------------------------------------------------------------------------- 1 | * { 2 | -moz-box-sizing: border-box; 3 | box-sizing: border-box; 4 | } 5 | 6 | body { 7 | font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif; 8 | font-size: 16px; 9 | font-weight: normal; 10 | margin: 3em 0; 11 | padding: 0; 12 | } 13 | 14 | .outer { 15 | margin: 0 auto; 16 | max-width: 480px; 17 | } 18 | 19 | .logo { 20 | background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0ZWQgYnkgSWNvTW9vbi5pbyAtLT4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIj4KPGcgaWQ9Imljb21vb24taWdub3JlIj4KCTxsaW5lIHN0cm9rZS13aWR0aD0iMSIgeDE9IiIgeTE9IiIgeDI9IiIgeTI9IiIgc3Ryb2tlPSIjNDQ5RkRCIiBvcGFjaXR5PSIiPjwvbGluZT4KPC9nPgoJPHBhdGggZD0iTTI2LjUyIDI0LjcwNWwtNi41Mi0xMS4yOTN2LTcuODc3aDAuNzVjMC42OSAwIDEuMjUtMC41NiAxLjI1LTEuMjVzLTAuNTYtMS4yNS0xLjI1LTEuMjVoLTkuNWMtMC42OSAwLTEuMjUgMC41Ni0xLjI1IDEuMjVzMC41NiAxLjI1IDEuMjUgMS4yNWgwLjc1djcuODc3bC02LjUyIDExLjI5M2MtMS4zNzUgMi4zODEtMC4yNSA0LjMzIDIuNSA0LjMzaDE2LjA0MGMyLjc1IDAgMy44NzUtMS45NDkgMi41LTQuMzN6TTE0LjUgNS41MzVoM2wtMC4wMDIgOC41NDMgMS45OTYgMy40NTdoLTYuOTljMS4xMjMtMS45NDYgMS45OTYtMy40NTkgMS45OTYtMy40NTl2LTguNTQxek0yNC4wMjAgMjYuNTM1aC0xNi4wNDBjLTAuMjU5IDAtMC40NDMtMC4wMzAtMC41NjEtMC4wNjMgMC4wMzEtMC4xMTkgMC4wOTctMC4yOTQgMC4yMjctMC41MTggMCAwIDEuNzA2LTIuOTU3IDMuNDE2LTUuOTJsOS44NzYtMCAzLjQxOCA1LjkyYzAuMTI5IDAuMjI0IDAuMTk1IDAuMzk5IDAuMjI3IDAuNTE4LTAuMTE4IDAuMDMyLTAuMzAzIDAuMDYzLTAuNTYyIDAuMDYzeiIgZmlsbD0iIzAwMDAwMCI+PC9wYXRoPgo8L3N2Zz4K'); 21 | background-position: center center; 22 | background-repeat: no-repeat; 23 | background-size: contain; 24 | height: 1.5em; 25 | margin: 0 auto .75em; 26 | width: 1.5em; 27 | } 28 | 29 | .title { 30 | font-size: 1.5em; 31 | font-weight: 600; 32 | letter-spacing: .3em; 33 | margin: 0 0 .25em; 34 | text-align: center; 35 | text-indent: .3em; 36 | text-transform: uppercase; 37 | } 38 | 39 | .subtitle { 40 | color: #999; 41 | font-size: .875em; 42 | margin-bottom: 2em; 43 | text-align: center; 44 | } 45 | 46 | .leaderboard { 47 | border-top: 1px solid #f1f1f1; 48 | counter-reset: ol-counter; 49 | list-style-type: none; 50 | margin: 0 0 1.5em; 51 | padding: 0; 52 | } 53 | 54 | .player { 55 | border-bottom: 1px solid #f1f1f1; 56 | cursor: pointer; 57 | padding: .5em 0; 58 | position: relative; 59 | overflow: hidden; 60 | transition: background 300ms ease-out; 61 | border-left: 4px solid white; 62 | } 63 | 64 | .player:before { 65 | color: #999; 66 | content: counter(ol-counter); 67 | counter-increment: ol-counter; 68 | display: inline-block; 69 | font-weight: 300; 70 | line-height: 3em; 71 | text-align: center; 72 | vertical-align: middle; 73 | width: 3em; 74 | } 75 | 76 | .player .avatar { 77 | display: inline-block; 78 | margin-right: 1em; 79 | vertical-align: middle; 80 | } 81 | 82 | .player .name { 83 | display: inline-block; 84 | font-size: 1.25em; 85 | font-weight: 300; 86 | vertical-align: middle; 87 | } 88 | 89 | .player .score { 90 | color: #333; 91 | display: block; 92 | float: right; 93 | font-size: 1.25em; 94 | font-weight: 600; 95 | line-height: 2.4em; 96 | padding-right: 1.25em; 97 | } 98 | 99 | .player.selected { 100 | background-color: #fefff4; 101 | border-left: #eb5f3a 4px solid; 102 | } 103 | 104 | .player:hover { 105 | background-color: #fefff4; 106 | } 107 | 108 | .details { 109 | overflow: hidden; 110 | } 111 | 112 | .details .name { 113 | display: inline-block; 114 | font-size: 1.5em; 115 | font-weight: 300; 116 | line-height: 2.25rem; 117 | padding-left: 1.25rem; 118 | vertical-align: middle; 119 | } 120 | 121 | .details .inc { 122 | border-radius: 3em; 123 | border: #eb5f3a 1px solid; 124 | background: transparent; 125 | color: #eb5f3a; 126 | cursor: pointer; 127 | float: right; 128 | font-family: 'Source Sans Pro' ,'Helvetica Neue', Helvetica, Arial, sans-serif; 129 | font-size: 1rem; 130 | line-height: 1; 131 | margin: 0; 132 | outline: none; 133 | padding: 10px 30px; 134 | transition: all 200ms ease-in; 135 | } 136 | 137 | .inc:hover { 138 | background: #eb5f3a; 139 | color: #fff; 140 | } 141 | 142 | .inc:active { 143 | box-shadow: rgba(0,0,0,.3) 0 1px 3px 0 inset; 144 | } 145 | 146 | .message { 147 | color: #aaa; 148 | line-height: 2.25rem; 149 | text-align: center; 150 | } 151 | 152 | @media (max-width: 500px) { 153 | .details, .message { 154 | display: block; 155 | position: fixed; 156 | bottom: 0; 157 | background-color: #fafafa; 158 | width: 100%; 159 | padding: 12px 15px; 160 | border-top: 1px solid #ccc; 161 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); 162 | } 163 | 164 | .details .name { 165 | font-size: 1.2em; 166 | padding-left: 0; 167 | } 168 | 169 | .details .inc { 170 | padding: 10px 20px; 171 | } 172 | 173 | body { 174 | margin: 2em 0 4em 0; 175 | } 176 | 177 | .player:hover { 178 | background-color: inherit; 179 | } 180 | 181 | .player.selected:hover { 182 | background-color: #fefff4; 183 | } 184 | } -------------------------------------------------------------------------------- /leaderboard.html: -------------------------------------------------------------------------------- 1 | 2 | Leaderboard 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |

Leaderboard

11 |
Select a scientist to give them points
12 | {{> leaderboard}} 13 |
14 |
15 | 16 | 17 | 33 | 34 | 40 | -------------------------------------------------------------------------------- /leaderboard.js: -------------------------------------------------------------------------------- 1 | // Set up a collection to contain player information. On the server, 2 | // it is backed by a MongoDB collection named "players". 3 | 4 | Players = new Mongo.Collection("players"); 5 | 6 | if (Meteor.isClient) { 7 | // events 8 | 9 | Template.leaderboard.events({ 10 | 'click .inc': function () { 11 | var playerId = store.getReactiveState('selectedPlayerId'); 12 | store.dispatch( Actions.incrementScore(playerId) ); 13 | } 14 | }); 15 | 16 | Template.player.events({ 17 | 'click': function () { 18 | store.dispatch( Actions.selectPlayer(this._id) ); 19 | } 20 | }); 21 | 22 | // helpers 23 | 24 | Template.leaderboard.helpers({ 25 | players: function () { 26 | return Players.find({}, { sort: { score: -1, name: 1 } }); 27 | } 28 | }); 29 | 30 | Template.player.helpers({ 31 | selected: function () { 32 | var playerId = store.getReactiveState('selectedPlayerId'); 33 | return (playerId === this._id) ? 'selected' : ''; 34 | } 35 | }); 36 | } 37 | 38 | // On server startup, create some players if the database is empty. 39 | if (Meteor.isServer) { 40 | Meteor.startup(function () { 41 | if (Players.find().count() === 0) { 42 | var names = ["Ada Lovelace", "Grace Hopper", "Marie Curie", 43 | "Carl Friedrich Gauss", "Nikola Tesla", "Claude Shannon"]; 44 | _.each(names, function (name) { 45 | Players.insert({ 46 | name: name, 47 | score: Math.floor(Random.fraction() * 10) * 5 48 | }); 49 | }); 50 | } 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /packages.json: -------------------------------------------------------------------------------- 1 | { 2 | "externalify": "0.1.0", 3 | "redux": "1.0.1", 4 | "react-redux": "0.9.0", 5 | "redux-devtools": "1.1.1" 6 | } 7 | -------------------------------------------------------------------------------- /packages/npm-container/.npm/package/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/npm-container/.npm/package/README: -------------------------------------------------------------------------------- 1 | This directory and the files immediately inside it are automatically generated 2 | when you change this package's NPM dependencies. Commit the files in this 3 | directory (npm-shrinkwrap.json, .gitignore, and this README) to source control 4 | so that others run the same versions of sub-dependencies. 5 | 6 | You should NOT check in the node_modules directory that Meteor automatically 7 | creates; if you are using git, the .gitignore file tells git to ignore it. 8 | -------------------------------------------------------------------------------- /packages/npm-container/.npm/package/npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "externalify": { 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "globo": { 7 | "version": "1.0.2", 8 | "dependencies": { 9 | "accessory": { 10 | "version": "1.0.1", 11 | "dependencies": { 12 | "dot-parts": { 13 | "version": "1.0.1" 14 | } 15 | } 16 | }, 17 | "is-defined": { 18 | "version": "1.0.0" 19 | }, 20 | "ternary": { 21 | "version": "1.0.0" 22 | } 23 | } 24 | }, 25 | "map-obj": { 26 | "version": "1.0.1" 27 | }, 28 | "replace-require-functions": { 29 | "version": "1.0.0", 30 | "dependencies": { 31 | "detective": { 32 | "version": "4.1.1", 33 | "dependencies": { 34 | "acorn": { 35 | "version": "1.2.2" 36 | }, 37 | "defined": { 38 | "version": "1.0.0" 39 | }, 40 | "escodegen": { 41 | "version": "1.6.1", 42 | "dependencies": { 43 | "estraverse": { 44 | "version": "1.9.3" 45 | }, 46 | "esutils": { 47 | "version": "1.1.6" 48 | }, 49 | "esprima": { 50 | "version": "1.2.5" 51 | }, 52 | "optionator": { 53 | "version": "0.5.0", 54 | "dependencies": { 55 | "prelude-ls": { 56 | "version": "1.1.2" 57 | }, 58 | "deep-is": { 59 | "version": "0.1.3" 60 | }, 61 | "wordwrap": { 62 | "version": "0.0.3" 63 | }, 64 | "type-check": { 65 | "version": "0.3.1" 66 | }, 67 | "levn": { 68 | "version": "0.2.5" 69 | }, 70 | "fast-levenshtein": { 71 | "version": "1.0.6" 72 | } 73 | } 74 | }, 75 | "source-map": { 76 | "version": "0.1.43", 77 | "dependencies": { 78 | "amdefine": { 79 | "version": "1.0.0" 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | }, 87 | "has-require": { 88 | "version": "1.2.1", 89 | "dependencies": { 90 | "escape-string-regexp": { 91 | "version": "1.0.3" 92 | } 93 | } 94 | }, 95 | "patch-text": { 96 | "version": "1.0.2" 97 | }, 98 | "xtend": { 99 | "version": "4.0.0" 100 | } 101 | } 102 | }, 103 | "through2": { 104 | "version": "0.4.2", 105 | "dependencies": { 106 | "readable-stream": { 107 | "version": "1.0.33", 108 | "dependencies": { 109 | "core-util-is": { 110 | "version": "1.0.1" 111 | }, 112 | "isarray": { 113 | "version": "0.0.1" 114 | }, 115 | "string_decoder": { 116 | "version": "0.10.31" 117 | }, 118 | "inherits": { 119 | "version": "2.0.1" 120 | } 121 | } 122 | }, 123 | "xtend": { 124 | "version": "2.1.2", 125 | "dependencies": { 126 | "object-keys": { 127 | "version": "0.4.0" 128 | } 129 | } 130 | } 131 | } 132 | }, 133 | "transformify": { 134 | "version": "0.1.2", 135 | "dependencies": { 136 | "readable-stream": { 137 | "version": "1.1.13", 138 | "dependencies": { 139 | "core-util-is": { 140 | "version": "1.0.1" 141 | }, 142 | "isarray": { 143 | "version": "0.0.1" 144 | }, 145 | "string_decoder": { 146 | "version": "0.10.31" 147 | }, 148 | "inherits": { 149 | "version": "2.0.1" 150 | } 151 | } 152 | } 153 | } 154 | } 155 | } 156 | }, 157 | "react-redux": { 158 | "version": "0.9.0", 159 | "dependencies": { 160 | "invariant": { 161 | "version": "2.2.1", 162 | "dependencies": { 163 | "loose-envify": { 164 | "version": "1.2.0", 165 | "dependencies": { 166 | "js-tokens": { 167 | "version": "1.0.3" 168 | } 169 | } 170 | } 171 | } 172 | } 173 | } 174 | }, 175 | "redux": { 176 | "version": "1.0.1" 177 | }, 178 | "redux-devtools": { 179 | "version": "1.1.1", 180 | "dependencies": { 181 | "react-mixin": { 182 | "version": "1.7.0", 183 | "dependencies": { 184 | "object-assign": { 185 | "version": "2.1.1" 186 | }, 187 | "smart-mixin": { 188 | "version": "1.2.1" 189 | } 190 | } 191 | } 192 | } 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /packages/npm-container/index.js: -------------------------------------------------------------------------------- 1 | Meteor.npmRequire = function(moduleName) { 2 | var module = Npm.require(moduleName); 3 | return module; 4 | }; 5 | 6 | Meteor.require = function(moduleName) { 7 | console.warn('Meteor.require is deprecated. Please use Meteor.npmRequire instead!'); 8 | return Meteor.npmRequire(moduleName); 9 | }; -------------------------------------------------------------------------------- /packages/npm-container/package.js: -------------------------------------------------------------------------------- 1 | var path = Npm.require('path'); 2 | var fs = Npm.require('fs'); 3 | 4 | Package.describe({ 5 | summary: 'Contains all your npm dependencies', 6 | version: '1.0.0', 7 | name: 'npm-container' 8 | }); 9 | 10 | var packagesJsonFile = path.resolve('./packages.json'); 11 | try { 12 | var fileContent = fs.readFileSync(packagesJsonFile); 13 | var packages = JSON.parse(fileContent.toString()); 14 | Npm.depends(packages); 15 | } catch (ex) { 16 | console.error('ERROR: packages.json parsing error [ ' + ex.message + ' ]'); 17 | } 18 | 19 | // Adding the app's packages.json as a used file for this package will get 20 | // Meteor to watch it and reload this package when it changes 21 | Package.onUse(function(api) { 22 | api.add_files(['index.js', '../../packages.json'], 'server'); 23 | }); --------------------------------------------------------------------------------