├── .gitignore ├── README.md ├── addProgressPlugin.js ├── app ├── client │ ├── components │ │ ├── App.css │ │ ├── App.tsx │ │ ├── BlazeTemplate.tsx │ │ ├── TaskComponent.tsx │ │ └── simplest-todo-react.css │ └── index.tsx ├── collections │ └── simplest-todos-react.ts ├── global.ts └── server │ └── index.ts ├── debug.js ├── deploy.js ├── dev.js ├── dirs.js ├── met ├── meteor ├── .meteor │ ├── .finished-upgraders │ ├── .gitignore │ ├── .id │ ├── packages │ ├── platforms │ ├── release │ └── versions ├── client │ ├── .gitignore │ └── loadClientBundle.html ├── meteor.html └── server │ └── .gitignore ├── package.json ├── predeploy.js ├── prod.js ├── projectName.js ├── settings ├── devel.json ├── prod.json └── stage.json ├── simplest-todo.iml ├── statsOptions.js ├── tsconfig.json ├── typings.json ├── typings └── meteor │ └── meteor-react.d.ts └── webpack ├── devProps.js ├── loadClientBundle.html ├── webpack.config.client.dev.js ├── webpack.config.client.js ├── webpack.config.client.prod.js ├── webpack.config.server.dev.js ├── webpack.config.server.js └── webpack.config.server.prod.js /.gitignore: -------------------------------------------------------------------------------- 1 | # project custom 2 | 3 | 4 | typings/* 5 | !typings/meteor/ 6 | 7 | webpack/assets/* 8 | *.iml 9 | # vs code 10 | rwc-report.html 11 | *.swp 12 | build.json 13 | *.actual 14 | tests/webhost/*.d.ts 15 | tests/webhost/webtsc.js 16 | tests/*.js 17 | tests/*.js.map 18 | tests/*.d.ts 19 | *.config 20 | coverage/ 21 | internal/ 22 | **/.DS_Store 23 | .settings 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | 28 | # Windows image file caches 29 | Thumbs.db 30 | ehthumbs.db 31 | 32 | # Folder config file 33 | Desktop.ini 34 | 35 | # Recycle Bin used on file shares 36 | $RECYCLE.BIN/ 37 | 38 | # Windows Installer files 39 | *.cab 40 | *.msi 41 | *.msm 42 | *.msp 43 | 44 | # Windows shortcuts 45 | *.lnk 46 | 47 | 48 | # Logs 49 | logs 50 | *.log 51 | npm-debug.log* 52 | 53 | # Runtime data 54 | pids 55 | *.pid 56 | *.seed 57 | 58 | # Directory for instrumented libs generated by jscoverage/JSCover 59 | lib-cov 60 | 61 | # Coverage directory used by tools like istanbul 62 | coverage 63 | 64 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 65 | .grunt 66 | 67 | # node-waf configuration 68 | .lock-wscript 69 | 70 | # Compiled binary addons (http://nodejs.org/api/addons.html) 71 | build/Release 72 | 73 | # Dependency directory 74 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 75 | node_modules 76 | 77 | # Optional npm cache directory 78 | .npm 79 | 80 | # Optional REPL history 81 | .node_repl_history 82 | 83 | .idea 84 | node 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # meteor-react-typescript-todo 2 | 3 | > this repository is heavily inspired by two other sources: 4 | > 5 | > [meteor-webpack-react-typescript](https://github.com/ipy/meteor-webpack-react-typescript/blob/master/README.md) 6 | > [boilerplate-webpack-typescript-react-meteor](https://github.com/julius/boilerplate-webpack-typescript-react-meteor) 7 | 8 | 9 | ## Description 10 | 11 | This is a tranformation of meteors official Todo tutorial to work with typescript and the react 12 | renderer. It tries to have the minimal number of dependencies and least complex build mechanism 13 | while still trying to give a pleasant development experience 14 | 15 | - Auto build on save 16 | - Auto refresh browser on change 17 | - Keep state on the client even when client side code changes 18 | 19 | 20 | 21 | ## Usage: 22 | ```sh 23 | git clone https://github.com/Aranir/meteor-react-typescript-todo.git 24 | cd meteor-react-typescript-todo 25 | typings install 26 | npm start 27 | ``` 28 | 29 | ## More: 30 | See the [origin JavaScript project from jedwards1211](https://github.com/jedwards1211/meteor-webpack-react) for more details. 31 | -------------------------------------------------------------------------------- /addProgressPlugin.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | // this is copped straight from webpack source 4 | module.exports = function addProgressPlugin(options) { 5 | if (!options.plugins) options.plugins = []; 6 | 7 | var chars = 0, lastState, lastStateTime; 8 | options.plugins.push(new webpack.ProgressPlugin(function(percentage, msg) { 9 | var state = msg; 10 | if(percentage < 1) { 11 | percentage = Math.floor(percentage * 100); 12 | msg = percentage + "% " + msg; 13 | if(percentage < 100) { 14 | msg = " " + msg; 15 | } 16 | if(percentage < 10) { 17 | msg = " " + msg; 18 | } 19 | } 20 | if(options.profile) { 21 | state = state.replace(/^\d+\/\d+\s+/, ""); 22 | if(percentage === 0) { 23 | lastState = null; 24 | lastStateTime = +new Date(); 25 | } else if(state !== lastState || percentage === 1) { 26 | var now = +new Date(); 27 | if(lastState) { 28 | var stateMsg = (now - lastStateTime) + "ms " + lastState; 29 | goToLineStart(stateMsg); 30 | process.stderr.write(stateMsg + "\n"); 31 | chars = 0; 32 | } 33 | lastState = state; 34 | lastStateTime = now; 35 | } 36 | } 37 | goToLineStart(msg); 38 | process.stderr.write(msg); 39 | })); 40 | function goToLineStart(nextMessage) { 41 | var str = ""; 42 | for(; chars > nextMessage.length; chars--) { 43 | str += "\b \b"; 44 | } 45 | chars = nextMessage.length; 46 | for(var i = 0; i < chars; i++) { 47 | str += "\b"; 48 | } 49 | if(str) process.stderr.write(str); 50 | } 51 | } -------------------------------------------------------------------------------- /app/client/components/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | font-size: 20px; 4 | } 5 | -------------------------------------------------------------------------------- /app/client/components/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import * as reactMixin from 'react-mixin'; 4 | import TaskComponent from './TaskComponent'; 5 | import BlazeTemplate from './BlazeTemplate'; 6 | import './App.css'; 7 | import './simplest-todo-react.css'; 8 | import {Task, Tasks} from './../../collections/simplest-todos-react'; 9 | import {TaskCalls} from "../../global"; 10 | 11 | Meteor.call('sayHello', function (err, res) { 12 | console.log(res); 13 | }); 14 | 15 | 16 | interface AppState { 17 | hideCompleted; 18 | } 19 | 20 | interface Data { 21 | tasks: Array; 22 | incompleteCount: number; 23 | currentUser: Meteor.User; 24 | } 25 | 26 | 27 | @reactMixin.decorate(ReactMeteorData) 28 | export default class App extends React.Component { 29 | 30 | data:Data; 31 | 32 | constructor(props:any) { 33 | super(props); 34 | this.state = {hideCompleted: false}; 35 | } 36 | 37 | getMeteorData() { 38 | let query = {}; 39 | 40 | if (this.state.hideCompleted) { 41 | query = {checked: {$ne: true}}; 42 | } 43 | return { 44 | tasks: Tasks.find(query, {sort: {createdAt: -1}}).fetch(), 45 | incompleteCount: Tasks.find(query).count(), 46 | currentUser: Meteor.user() 47 | }; 48 | } 49 | 50 | renderTasks() { 51 | return this.data.tasks.map((task: Task) => { 52 | const currentUserId = this.data.currentUser && this.data.currentUser._id; 53 | const showPrivateButton = task.owner === currentUserId; 54 | 55 | return ; 60 | }); 61 | } 62 | 63 | handleSubmit(event) { 64 | event.preventDefault(); 65 | 66 | // Find the text field via the React ref 67 | let element:HTMLInputElement = ReactDOM.findDOMNode(this.refs['textInput']); 68 | var text = element.value.trim(); 69 | Meteor.call(TaskCalls.ADD_TASK, text); 70 | 71 | // Clear form 72 | element.value = ""; 73 | } 74 | 75 | toggleHideCompleted() { 76 | this.setState({ 77 | hideCompleted: !this.state.hideCompleted 78 | }); 79 | } 80 | 81 | render() { 82 | var template = window['Template']; 83 | return ( 84 | 85 |
86 | 87 | 88 |
89 |

Todo List ({this.data.incompleteCount})

90 |
91 | 92 | 93 | 101 | { this.data.currentUser ? ( 102 | this.createTaskForm() 103 | ) : null } 104 | 105 | 106 | 107 |
    108 | {this.renderTasks()} 109 |
110 |
111 | ); 112 | } 113 | 114 | private createTaskForm() { 115 | return ( 116 |
117 | 121 |
122 | ); 123 | } 124 | 125 | } 126 | 127 | -------------------------------------------------------------------------------- /app/client/components/BlazeTemplate.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | 5 | interface BlazeTemplateProps extends React.Props { 6 | template: any, 7 | component?: any, 8 | } 9 | 10 | export default class BlazeTemplate extends React.Component { 11 | static propTypes: React.ValidationMap = { 12 | template: React.PropTypes.any.isRequired, 13 | } 14 | 15 | static defaultProps = { 16 | component: 'div', 17 | } 18 | 19 | view: Blaze.View; 20 | 21 | // we don't want to re-render this component if parent changes 22 | shouldComponentUpdate() { 23 | return false; 24 | } 25 | componentDidMount() { 26 | let {template} = this.props; 27 | this.view = Blaze.render(template, ReactDOM.findDOMNode(this.refs['root'])); 28 | } 29 | componentWillUnmount() { 30 | Blaze.remove(this.view); 31 | } 32 | render() { 33 | let Component = this.props.component; 34 | return ; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/client/components/TaskComponent.tsx: -------------------------------------------------------------------------------- 1 | 2 | import * as React from 'react'; 3 | import {Task, Tasks} from "../../collections/simplest-todos-react"; 4 | import './App.css'; 5 | import {TaskCalls} from "../../global"; 6 | 7 | class TaskProps { 8 | public key: string; 9 | public task: Task; 10 | public showPrivateButton: boolean; 11 | } 12 | 13 | export default class TaskComponent extends React.Component { 14 | constructor(props: TaskProps) { 15 | super(props); 16 | } 17 | 18 | deleteThisTask() { 19 | Meteor.call(TaskCalls.REMOVE_TASK, this.props.task._id) 20 | } 21 | 22 | toggleChecked() { 23 | Meteor.call(TaskCalls.SET_CHECKED, this.props.task._id, !this.props.task.checked); 24 | } 25 | 26 | tooglePrivate() { 27 | Meteor.call(TaskCalls.SET_PRIVATE, this.props.task._id, ! this.props.task.private); 28 | } 29 | render() { 30 | 31 | const taskClassName = (this.props.task.checked ? 'checked' : '') + " " + 32 | (this.props.task.private ? "private" : ""); 33 | return ( 34 |
  • 35 | 38 | 39 | 44 | {this.props.showPrivateButton ? 45 | ( 46 | 49 | ) : 50 | null 51 | } 52 | 53 | {this.props.task.username}: {this.props.task.text} 54 | 55 |
  • 56 | ); 57 | } 58 | } -------------------------------------------------------------------------------- /app/client/components/simplest-todo-react.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ 2 | body { 3 | font-family: sans-serif; 4 | background-color: #315481; 5 | background-image: linear-gradient(to bottom, #315481, #918e82 100%); 6 | background-attachment: fixed; 7 | 8 | position: absolute; 9 | top: 0; 10 | bottom: 0; 11 | left: 0; 12 | right: 0; 13 | 14 | padding: 0; 15 | margin: 0; 16 | 17 | font-size: 14px; 18 | } 19 | 20 | .container { 21 | max-width: 600px; 22 | margin: 0 auto; 23 | min-height: 100%; 24 | background: white; 25 | } 26 | 27 | header { 28 | background: #d2edf4; 29 | background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%); 30 | padding: 20px 15px 15px 15px; 31 | position: relative; 32 | } 33 | 34 | #login-buttons { 35 | display: block; 36 | } 37 | 38 | h1 { 39 | font-size: 1.5em; 40 | margin: 0; 41 | margin-bottom: 10px; 42 | display: inline-block; 43 | margin-right: 1em; 44 | } 45 | 46 | form { 47 | margin-top: 10px; 48 | margin-bottom: -10px; 49 | position: relative; 50 | } 51 | 52 | .new-task input { 53 | box-sizing: border-box; 54 | padding: 10px 0; 55 | background: transparent; 56 | border: none; 57 | width: 100%; 58 | padding-right: 80px; 59 | font-size: 1em; 60 | } 61 | 62 | .new-task input:focus{ 63 | outline: 0; 64 | } 65 | 66 | ul { 67 | margin: 0; 68 | padding: 0; 69 | background: white; 70 | } 71 | 72 | .delete { 73 | float: right; 74 | font-weight: bold; 75 | background: none; 76 | font-size: 1em; 77 | border: none; 78 | position: relative; 79 | } 80 | 81 | li { 82 | position: relative; 83 | list-style: none; 84 | padding: 15px; 85 | border-bottom: #eee solid 1px; 86 | } 87 | 88 | li .text { 89 | margin-left: 10px; 90 | } 91 | 92 | li.checked { 93 | color: #888; 94 | } 95 | 96 | li.checked .text { 97 | text-decoration: line-through; 98 | } 99 | 100 | li.private { 101 | background: #eee; 102 | border-color: #ddd; 103 | } 104 | 105 | header .hide-completed { 106 | float: right; 107 | } 108 | 109 | .toggle-private { 110 | margin-left: 5px; 111 | } 112 | 113 | @media (max-width: 600px) { 114 | li { 115 | padding: 12px 15px; 116 | } 117 | 118 | .search { 119 | width: 150px; 120 | clear: both; 121 | } 122 | 123 | .new-task input { 124 | padding-bottom: 5px; 125 | } 126 | } -------------------------------------------------------------------------------- /app/client/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import App from './components/App'; 4 | 5 | console.log('Running on client only'); 6 | 7 | Accounts.ui.config({ 8 | passwordSignupFields: "USERNAME_ONLY" 9 | }); 10 | 11 | Meteor.subscribe('tasks', Meteor.userId()); 12 | 13 | Meteor.startup(() => { 14 | ReactDOM.render(, document.getElementById('render-target')); 15 | }); 16 | -------------------------------------------------------------------------------- /app/collections/simplest-todos-react.ts: -------------------------------------------------------------------------------- 1 | export interface Task { 2 | _id?: string; 3 | text: string; 4 | createdAt: Date; 5 | checked: boolean; 6 | owner: string; 7 | username: string; 8 | private: boolean; 9 | } 10 | 11 | export const Tasks = new Mongo.Collection('tasks'); -------------------------------------------------------------------------------- /app/global.ts: -------------------------------------------------------------------------------- 1 | export class TaskCalls { 2 | public static get ADD_TASK(): string {return 'addTask'}; 3 | public static get REMOVE_TASK(): string {return 'removeTask'} 4 | public static get SET_CHECKED(): string {return 'setChecked'} 5 | public static get SET_PRIVATE(): string {return 'setPrivate'} 6 | 7 | } -------------------------------------------------------------------------------- /app/server/index.ts: -------------------------------------------------------------------------------- 1 | import {Task, Tasks} from '../collections/simplest-todos-react'; 2 | 3 | console.log("server is running"); 4 | 5 | //The () => function declaration can't be used here as we need 6 | //*this* to be modified inside the function 7 | Meteor.publish('tasks', function() { 8 | console.log("Current server user: " + this.userId); 9 | return Tasks.find({ 10 | $or: [{ 11 | private: {$ne: true}}, 12 | {owner: this.userId} 13 | ] 14 | }); 15 | }); 16 | 17 | Meteor.methods({ 18 | addTask(text) { 19 | // Make sure the user is logged in before inserting a task 20 | if (! Meteor.userId()) { 21 | throw new Meteor.Error("not-authorized"); 22 | } 23 | 24 | Tasks.insert({ 25 | text: text, 26 | createdAt: new Date(), 27 | owner: Meteor.userId(), 28 | checked: false, 29 | username: Meteor.user().username, 30 | private: false 31 | }); 32 | }, 33 | 34 | removeTask(taskId) { 35 | const task: Task = Tasks.findOne(taskId); 36 | if (task.private && task.owner !== Meteor.userId()) { 37 | // If the task is private, make sure only the owner can delete it 38 | throw new Meteor.Error("not-authorized"); 39 | } 40 | 41 | Tasks.remove(taskId); 42 | }, 43 | 44 | setChecked(taskId, setChecked) { 45 | const task = Tasks.findOne(taskId); 46 | if (task.private && task.owner !== Meteor.userId()) { 47 | // If the task is private, make sure only the owner can check it off 48 | throw new Meteor.Error("not-authorized"); 49 | } 50 | 51 | Tasks.update(taskId, { $set: { checked: setChecked} }); 52 | }, 53 | 54 | setPrivate(taskId: string, setToPrivate: boolean) { 55 | const task: Task = Tasks.findOne(taskId); 56 | 57 | if (task.owner !== Meteor.userId()) { 58 | throw new Meteor.Error("not-authorized"); 59 | } 60 | 61 | Tasks.update(taskId, {$set: {private: setToPrivate}}); 62 | } 63 | }); -------------------------------------------------------------------------------- /debug.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | exec('node-inspector', {async: true}); 3 | env.NODE_OPTIONS = '--debug=5858'; 4 | require('./dev'); 5 | -------------------------------------------------------------------------------- /deploy.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | var path = require('path'); 3 | 4 | if (!process.argv[2]) { 5 | echo('See ' + path.basename(__filename) + ' to customize your deploy command'); 6 | return; 7 | } 8 | 9 | var projectName = require('./projectName'); 10 | if (!projectName) { 11 | echo('Please enter your project name in projectName.js'); 12 | } 13 | 14 | var dirs = require('./dirs'); 15 | 16 | echo(); 17 | echo('Building Webpack bundles for deployment...'); 18 | echo(); 19 | require('./predeploy')(function(err) { 20 | if (err) exit(1); 21 | deploy(); 22 | }); 23 | 24 | function deploy() { 25 | switch (process.argv[2]) { 26 | 27 | case 'meteor.com': 28 | cd(dirs.meteor); 29 | exec('meteor deploy ' + projectName + '.meteor.com', {async: true}); 30 | break; 31 | 32 | case 'modulus': 33 | env.METEOR_SETTINGS = cat('settings/prod.json'); 34 | cd(dirs.meteor); 35 | exec('modulus deploy --project-name ' + projectName, {async: true}); 36 | break; 37 | 38 | case 'mup': 39 | echo("Make sure to mup init and mup setup before first deploy"); 40 | /* 41 | * you will also need to move settings/prod.json to settings/prod/settings.json 42 | * then mup init inside settings/prod/ so that mup uses the new settings.json 43 | * this will require a settings path change in ./dev script 44 | */ 45 | cd('settings/prod'); 46 | exec('mup deploy', {async: true}); 47 | break; 48 | 49 | case 'demeteorizer': 50 | rm('-rf', 'dist/bundle'); 51 | mkdir('-p', 'dist/bundle'); 52 | cd(dirs.meteor); 53 | exec("demeteorizer -o ../dist/bundle --json '" + cat('../settings/prod.json') + "'", {async: true}); 54 | // run your own command to deploy to your server 55 | break; 56 | 57 | default: 58 | echo('See ' + path.basename(__filename) + ' to customize your deploy command'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /dev.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var dirs = require('./dirs'); 5 | var webpack = require('webpack'); 6 | var WebpackDevServer = require('webpack-dev-server'); 7 | var addProgressPlugin = require('./addProgressPlugin'); 8 | var statsOptions = require('./statsOptions'); 9 | 10 | var serverConfig = require(path.join(dirs.webpack, 'webpack.config.server.dev')); 11 | var clientConfig = require(path.join(dirs.webpack, 'webpack.config.client.dev')); 12 | 13 | if (!clientConfig.devServer) clientConfig.devServer = {}; 14 | if (!clientConfig.devServer.stats) clientConfig.devServer.stats = statsOptions; 15 | 16 | addProgressPlugin(serverConfig); 17 | addProgressPlugin(clientConfig); 18 | 19 | serverConfig.plugins.push(new webpack.BannerPlugin( 20 | 'require("source-map-support/register");\n' + 21 | 'var Npm = Meteor.__mwrContext__.Npm;\n' + 22 | 'var Assets = Meteor.__mwrContext__.Assets;\n' + 23 | 'delete Meteor.__mwrContext__;\n' + 24 | 'var require = Npm.require;\n', 25 | {raw: true} 26 | )); 27 | 28 | var serverBundlePath = path.join(dirs.assets, 'server.bundle.js'); 29 | var serverBundleRequirePath = serverBundlePath.replace(/\\/g, '\\\\'); 30 | var serverBundleLink = path.join(dirs.meteor, 'server/server.bundle.min.js'); 31 | var clientBundleLink = path.join(dirs.meteor, 'client/client.bundle.min.js'); 32 | var loadClientBundleHtml = path.join(dirs.webpack, 'loadClientBundle.html'); 33 | var loadClientBundleLink = path.join(dirs.meteor, 'client/loadClientBundle.html'); 34 | 35 | var requireServerBundleJs = path.join(dirs.meteor, 'server/require.server.bundle.js'); 36 | 37 | if (fs.existsSync(clientBundleLink)) rm(clientBundleLink); 38 | if (fs.existsSync(serverBundleLink)) rm(serverBundleLink); 39 | 40 | var serverCompiler = webpack(serverConfig); 41 | var serverBundleReady = false; 42 | 43 | serverCompiler.watch({ 44 | progress: true, 45 | colors: true, 46 | }, function(err, stats) { 47 | console.log(stats.toString(statsOptions)) ; 48 | updateRequireServerBundleJs(stats); 49 | if (!serverBundleReady) { 50 | serverBundleReady = true; 51 | compileClient(); 52 | runMeteor(); 53 | } 54 | }); 55 | 56 | function compileClient() { 57 | var clientCompiler = webpack(clientConfig); 58 | var clientDevServer = new WebpackDevServer(clientCompiler, clientConfig.devServer); 59 | 60 | clientDevServer.listen(clientConfig.devServer.port, clientConfig.devServer.host, function() {}); 61 | 62 | cp('-f', loadClientBundleHtml, loadClientBundleLink); 63 | } 64 | 65 | function runMeteor() { 66 | cd(dirs.meteor); 67 | exec('meteor --settings ../settings/devel.json', {async: true}); 68 | } 69 | 70 | function updateRequireServerBundleJs(stats) { 71 | var jsonStats = stats.toJson({hash: true}); 72 | ('//' + jsonStats.hash + '\n' + 73 | 'Meteor.__mwrContext__ = {Npm: Npm, Assets: Assets};\n' + 74 | 'Npm.require("' + serverBundleRequirePath + '");').to(requireServerBundleJs); 75 | } 76 | -------------------------------------------------------------------------------- /dirs.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | webpack: path.join(__dirname, 'webpack'), 5 | meteor: path.join(__dirname, 'meteor'), 6 | }; 7 | 8 | module.exports.assets= path.join(module.exports.webpack, 'assets'); 9 | -------------------------------------------------------------------------------- /met: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd meteor && meteor $@ 3 | -------------------------------------------------------------------------------- /meteor/.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 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | -------------------------------------------------------------------------------- /meteor/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /meteor/.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 | 1vv5u45aqssvg1mvwhf5 8 | -------------------------------------------------------------------------------- /meteor/.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 | meteor-base # Packages every Meteor app needs to have 8 | mobile-experience # Packages for a great mobile UX 9 | mongo # The database Meteor supports right now 10 | blaze-html-templates # Compile .html files into Meteor Blaze views 11 | session # Client-side reactive dictionary for your app 12 | jquery # Helpful client-side library 13 | tracker # Meteor's client-side reactive programming library 14 | 15 | standard-minifiers # JS/CSS minifiers run for production mode 16 | es5-shim # ECMAScript 5 compatibility for older browsers. 17 | ecmascript # Enable ECMAScript2015+ syntax in app code 18 | 19 | react 20 | accounts-ui 21 | accounts-password 22 | -------------------------------------------------------------------------------- /meteor/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /meteor/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.2.1 2 | -------------------------------------------------------------------------------- /meteor/.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.2 2 | accounts-password@1.1.4 3 | accounts-ui@1.1.6 4 | accounts-ui-unstyled@1.1.8 5 | autoupdate@1.2.4 6 | babel-compiler@5.8.24_1 7 | babel-runtime@0.1.4 8 | base64@1.0.4 9 | binary-heap@1.0.4 10 | blaze@2.1.3 11 | blaze-html-templates@1.0.1 12 | blaze-tools@1.0.4 13 | boilerplate-generator@1.0.4 14 | caching-compiler@1.0.0 15 | caching-html-compiler@1.0.2 16 | callback-hook@1.0.4 17 | check@1.1.0 18 | coffeescript@1.0.11 19 | cosmos:browserify@0.9.3 20 | ddp@1.2.2 21 | ddp-client@1.2.1 22 | ddp-common@1.2.2 23 | ddp-rate-limiter@1.0.0 24 | ddp-server@1.2.2 25 | deps@1.0.9 26 | diff-sequence@1.0.1 27 | ecmascript@0.1.6 28 | ecmascript-runtime@0.2.6 29 | ejson@1.0.7 30 | email@1.0.8 31 | es5-shim@4.1.14 32 | fastclick@1.0.7 33 | geojson-utils@1.0.4 34 | hot-code-push@1.0.0 35 | html-tools@1.0.5 36 | htmljs@1.0.5 37 | http@1.1.1 38 | id-map@1.0.4 39 | jquery@1.11.4 40 | jsx@0.2.3 41 | launch-screen@1.0.4 42 | less@2.5.1 43 | livedata@1.0.15 44 | localstorage@1.0.5 45 | logging@1.0.8 46 | meteor@1.1.10 47 | meteor-base@1.0.1 48 | minifiers@1.1.7 49 | minimongo@1.0.10 50 | mobile-experience@1.0.1 51 | mobile-status-bar@1.0.6 52 | mongo@1.1.3 53 | mongo-id@1.0.1 54 | npm-bcrypt@0.7.8_2 55 | npm-mongo@1.4.39_1 56 | observe-sequence@1.0.7 57 | ordered-dict@1.0.4 58 | promise@0.5.1 59 | random@1.0.5 60 | rate-limit@1.0.0 61 | react@0.14.3 62 | react-meteor-data@0.2.4 63 | react-runtime@0.14.4 64 | react-runtime-dev@0.14.4 65 | react-runtime-prod@0.14.4 66 | reactive-dict@1.1.3 67 | reactive-var@1.0.6 68 | reload@1.1.4 69 | retry@1.0.4 70 | routepolicy@1.0.6 71 | service-configuration@1.0.5 72 | session@1.1.1 73 | sha@1.0.4 74 | spacebars@1.0.7 75 | spacebars-compiler@1.0.7 76 | srp@1.0.4 77 | standard-minifiers@1.0.2 78 | templating@1.1.5 79 | templating-tools@1.0.0 80 | tracker@1.0.9 81 | ui@1.0.8 82 | underscore@1.0.4 83 | url@1.0.5 84 | webapp@1.2.3 85 | webapp-hashing@1.0.5 86 | -------------------------------------------------------------------------------- /meteor/client/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /meteor/client/loadClientBundle.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /meteor/meteor.html: -------------------------------------------------------------------------------- 1 | 2 | Todo List 3 | 4 | 5 | 6 |
    7 |
    8 |

    Loading...

    9 |
    10 |
    11 | -------------------------------------------------------------------------------- /meteor/server/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simplest-todo", 3 | "version": "0.0.1", 4 | "description": "Simple skeleton for meteor todo app with react and typescript", 5 | "repository": { 6 | "type": "git", 7 | "url": "git@github.com:Aranir/meteor-react-typescript-todo.git" 8 | }, 9 | "scripts": { 10 | "start": "node dev.js" 11 | }, 12 | "keywords": [ 13 | "meteor", 14 | "typescript", 15 | "react" 16 | ], 17 | "author": "Aranir", 18 | "license": "MIT", 19 | "dependencies": { 20 | "babel": "^6.3.26", 21 | "babel-core": "6.7.6", 22 | "babel-loader": "6.2.4", 23 | "babel-plugin-react-transform": "^2.0.0", 24 | "babel-preset-es2015": "^6.3.13", 25 | "babel-preset-react": "^6.3.13", 26 | "classnames": "^2.2.1", 27 | "core-js": "^2.0.1", 28 | "css-loader": "^0.23.1", 29 | "lodash": "^4.11.1", 30 | "node-libs-browser": "^1.0.0", 31 | "null-loader": "^0.1.1", 32 | "react": "^15.0.1", 33 | "react-dom": "^15.0.1", 34 | "react-mixin": "^3.0.3", 35 | "react-transform-catch-errors": "^1.0.1", 36 | "react-transform-hmr": "^1.0.1", 37 | "redbox-react": "^1.2.0", 38 | "regenerator": "^0.8.42", 39 | "shelljs": "^0.6.0", 40 | "source-map-support": "^0.4.0", 41 | "style-loader": "^0.13.0", 42 | "ts-loader": "^0.8.2", 43 | "typescript": "^1.7.5", 44 | "webpack": "^1.12.9", 45 | "webpack-dev-server": "^1.14.0" 46 | }, 47 | "devDependencies": { 48 | "babel-plugin-react-transform": "^2.0.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /predeploy.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var dirs = require('./dirs'); 6 | var webpack = require('webpack'); 7 | var addProgressPlugin = require('./addProgressPlugin'); 8 | var statsOptions = require('./statsOptions'); 9 | 10 | var serverConfig = require(path.join(dirs.webpack, 'webpack.config.server.prod')); 11 | var clientConfig = require(path.join(dirs.webpack, 'webpack.config.client.prod')); 12 | 13 | addProgressPlugin(serverConfig); 14 | addProgressPlugin(clientConfig); 15 | 16 | serverConfig.plugins.push(new webpack.BannerPlugin('var require = Npm.require;\n', {raw: true})); 17 | 18 | var serverBundlePath = path.join(dirs.assets, 'server.bundle.js'); 19 | var clientBundlePath = path.join(dirs.assets, 'client.bundle.js'); 20 | var serverBundleLink = path.join(dirs.meteor, 'server/server.bundle.min.js'); 21 | var clientBundleLink = path.join(dirs.meteor, 'client/client.bundle.min.js'); 22 | var loadClientBundleHtml = path.join(dirs.webpack, 'loadClientBundle.html'); 23 | var loadClientBundleLink = path.join(dirs.meteor, 'client/loadClientBundle.html'); 24 | var requireServerBundleJs = path.join(dirs.meteor, 'server/require.server.bundle.js'); 25 | 26 | module.exports = function(callback) { 27 | if (!process.env.NODE_ENV) { 28 | process.env.NODE_ENV = env.NODE_ENV = 'production'; 29 | } 30 | 31 | if (fs.existsSync(loadClientBundleLink)) rm(loadClientBundleLink); 32 | if (fs.existsSync(requireServerBundleJs)) rm(requireServerBundleJs); 33 | 34 | var serverCompiler = webpack(serverConfig); 35 | 36 | serverCompiler.run(function(err, stats) { 37 | if (err) { 38 | console.error(error); 39 | return callback(err); 40 | } 41 | console.log(stats.toString(statsOptions)); 42 | if (stats.toJson().errors.length) { 43 | return callback(new Error('Webpack reported compilation errors')); 44 | } 45 | cp('-f', serverBundlePath, serverBundleLink); 46 | compileClient(); 47 | }); 48 | 49 | function compileClient() { 50 | var clientCompiler = webpack(clientConfig); 51 | clientCompiler.run(function(err, stats) { 52 | if (err) { 53 | console.error(error); 54 | return callback(err); 55 | } 56 | console.log(stats.toString(statsOptions)); 57 | if (stats.toJson().errors.length) { 58 | return callback(new Error('Webpack reported compilation errors')); 59 | } 60 | cp('-f', clientBundlePath, clientBundleLink); 61 | return callback(); 62 | }); 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /prod.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | if (!process.env.NODE_ENV) { 3 | process.env.NODE_ENV = env.NODE_ENV = 'production'; 4 | } 5 | 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | var dirs = require('./dirs'); 9 | var webpack = require('webpack'); 10 | var addProgressPlugin = require('./addProgressPlugin'); 11 | var statsOptions = require('./statsOptions'); 12 | 13 | var serverConfig = require(path.join(dirs.webpack, 'webpack.config.server.prod')); 14 | var clientConfig = require(path.join(dirs.webpack, 'webpack.config.client.prod')); 15 | 16 | addProgressPlugin(serverConfig); 17 | addProgressPlugin(clientConfig); 18 | 19 | serverConfig.plugins.push(new webpack.BannerPlugin('var require = Npm.require;\n', {raw: true})); 20 | 21 | var serverBundlePath = path.join(dirs.assets, 'server.bundle.js'); 22 | var clientBundlePath = path.join(dirs.assets, 'client.bundle.js'); 23 | var serverBundleLink = path.join(dirs.meteor, 'server/server.bundle.min.js'); 24 | var clientBundleLink = path.join(dirs.meteor, 'client/client.bundle.min.js'); 25 | var loadClientBundleHtml = path.join(dirs.webpack, 'loadClientBundle.html'); 26 | var loadClientBundleLink = path.join(dirs.meteor, 'client/loadClientBundle.html'); 27 | var requireServerBundleJs = path.join(dirs.meteor, 'server/require.server.bundle.js'); 28 | 29 | exec('node core-js-custom-build.js'); 30 | 31 | if (fs.existsSync(loadClientBundleLink)) rm(loadClientBundleLink); 32 | if (fs.existsSync(requireServerBundleJs)) rm(requireServerBundleJs); 33 | 34 | var serverCompiler = webpack(serverConfig); 35 | var serverBundleReady = false; 36 | var clientBundleReady = false; 37 | 38 | serverCompiler.watch(serverConfig.watchOptions || {}, function(err, stats) { 39 | console.log(stats.toString(statsOptions)); 40 | if (!serverBundleReady) { 41 | serverBundleReady = true; 42 | cp('-f', serverBundlePath, serverBundleLink); 43 | compileClient(); 44 | } 45 | }); 46 | 47 | function compileClient() { 48 | var clientCompiler = webpack(clientConfig); 49 | clientCompiler.watch(clientConfig.watchOptions || {}, function(err, stats) { 50 | console.log(stats.toString(statsOptions)); 51 | if (!clientBundleReady) { 52 | clientBundleReady = true; 53 | cp('-f', clientBundlePath, clientBundleLink); 54 | runMeteor(); 55 | } 56 | }); 57 | } 58 | 59 | function runMeteor() { 60 | cd(dirs.meteor); 61 | exec('meteor run --production --settings ../settings/prod.json', {async: true}); 62 | } 63 | -------------------------------------------------------------------------------- /projectName.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | module.exports = path.basename(__dirname); // replace with your project name -------------------------------------------------------------------------------- /settings/devel.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": { 3 | "env": "DEVEL", 4 | "foo": "bar" 5 | }, 6 | "secret": "memeda" 7 | } 8 | -------------------------------------------------------------------------------- /settings/prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": { 3 | "env": "PROD", 4 | "foo": "bar" 5 | }, 6 | "secret": "memeda" 7 | } 8 | 9 | -------------------------------------------------------------------------------- /settings/stage.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": { 3 | "env": "STAGE", 4 | "foo": "bar" 5 | }, 6 | "secret": "memeda" 7 | } 8 | -------------------------------------------------------------------------------- /simplest-todo.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /statsOptions.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | colors: true, 3 | chunkModules: false, 4 | modules: false, 5 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "preserve", 4 | "target": "ES6", 5 | "declaration": false, 6 | "module": "es2015", 7 | "experimentalDecorators": true, 8 | "sourceMap": true, 9 | "noLib": false, 10 | "outDir": "./build" 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | "meteor_core", 15 | "typings/browser", 16 | "typings/browser.d.ts" 17 | ] 18 | } 19 | 20 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simplest-todo", 3 | "version": false, 4 | "ambientDependencies": { 5 | "classnames": "github:DefinitelyTyped/DefinitelyTyped/classnames/classnames.d.ts#dc9dabe74a5be62613b17a3605309783a12ff28a", 6 | "react-mixin": "github:DefinitelyTyped/DefinitelyTyped/react-mixin/react-mixin.d.ts#dc9dabe74a5be62613b17a3605309783a12ff28a", 7 | "react": "github:DefinitelyTyped/DefinitelyTyped/react/react.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 8 | "lodash": "github:DefinitelyTyped/DefinitelyTyped/lodash/lodash.d.ts#dc9dabe74a5be62613b17a3605309783a12ff28a", 9 | "node": "github:DefinitelyTyped/DefinitelyTyped/node/node.d.ts#dc9dabe74a5be62613b17a3605309783a12ff28a", 10 | "jquery": "github:DefinitelyTyped/DefinitelyTyped/jquery/jquery.d.ts#dc9dabe74a5be62613b17a3605309783a12ff28a", 11 | "react-global": "github:DefinitelyTyped/DefinitelyTyped/react/react-global.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 12 | "react-addons-create-fragment": "github:DefinitelyTyped/DefinitelyTyped/react/react-addons-create-fragment.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 13 | "react-addons-css-transition-group": "github:DefinitelyTyped/DefinitelyTyped/react/react-addons-css-transition-group.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 14 | "react-addons-transition-group": "github:DefinitelyTyped/DefinitelyTyped/react/react-addons-transition-group.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 15 | "react-addons-perf": "github:DefinitelyTyped/DefinitelyTyped/react/react-addons-perf.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 16 | "react-addons-linked-state-mixin": "github:DefinitelyTyped/DefinitelyTyped/react/react-addons-linked-state-mixin.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 17 | "react-addons-pure-render-mixin": "github:DefinitelyTyped/DefinitelyTyped/react/react-addons-pure-render-mixin.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 18 | "react-addons-test-utils": "github:DefinitelyTyped/DefinitelyTyped/react/react-addons-test-utils.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 19 | "react-addons-update": "github:DefinitelyTyped/DefinitelyTyped/react/react-addons-update.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 20 | "react-dom": "github:DefinitelyTyped/DefinitelyTyped/react/react-dom.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a", 21 | "meteor": "github:DefinitelyTyped/DefinitelyTyped/meteor/meteor.d.ts#fe563dff3428bac1260d1794e2c2ecf8f097535a" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /typings/meteor/meteor-react.d.ts: -------------------------------------------------------------------------------- 1 | interface IReactMeteorData { 2 | data: any; 3 | } 4 | 5 | declare var ReactMeteorData: IReactMeteorData; -------------------------------------------------------------------------------- /webpack/devProps.js: -------------------------------------------------------------------------------- 1 | var host = '0.0.0.0'; 2 | var webpackPort = 9090; 3 | var meteorPort = 3000; 4 | 5 | module.exports = { 6 | host: host, 7 | webpackPort: webpackPort, 8 | meteorPort: meteorPort, 9 | baseUrl: 'http://' + host + ':' + webpackPort, 10 | contentBase: 'http://' + host + ':' + meteorPort, 11 | }; -------------------------------------------------------------------------------- /webpack/loadClientBundle.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /webpack/webpack.config.client.dev.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var config = require('./webpack.config.client'); 3 | var _ = require('lodash'); 4 | var devProps = require('./devProps'); 5 | 6 | var babelQuery = { 7 | cacheDirectory: true, 8 | 9 | presets: ['es2015', 'react'] 10 | , 11 | "plugins": [ 12 | // must be an array with options object as second item 13 | ["react-transform", { 14 | // must be an array of objects 15 | "transforms": [{ 16 | // can be an NPM module name or a local path 17 | "transform": "react-transform-hmr", 18 | // see transform docs for "imports" and "locals" dependencies 19 | "imports": ["react"], 20 | "locals": ["module"] 21 | }, { 22 | // you can have many transforms, not just one 23 | "transform": "react-transform-catch-errors", 24 | "imports": ["react", "redbox-react"] 25 | }] 26 | }] 27 | ] 28 | }; 29 | 30 | var config = module.exports = _.assign(_.clone(config), { 31 | devtool: 'eval', 32 | entry: [ 33 | 'webpack-dev-server/client?' + devProps.baseUrl, 34 | 'webpack/hot/only-dev-server', 35 | ].concat(config.entry), 36 | output: _.assign(_.clone(config.output), { 37 | publicPath: devProps.baseUrl + '/assets/', 38 | pathinfo: true, 39 | // crossOriginLoading is important since we are running 40 | // webpack-dev-server from a different port than Meteor 41 | crossOriginLoading: 'anonymous', 42 | }), 43 | module: { 44 | loaders: [ 45 | { 46 | test: /\.tsx?$/, 47 | loader: 'babel?' + JSON.stringify(babelQuery) + '!ts-loader', 48 | }, 49 | { 50 | test: /\.jsx?$/, 51 | loader: 'babel?' + JSON.stringify(babelQuery), 52 | exclude: /node_modules|lib/, 53 | }, 54 | { 55 | test: /\.css$/, 56 | loader: 'style!css', 57 | exclude: /node_modules|lib/, 58 | }, 59 | ], 60 | }, 61 | plugins: (config.plugins || []).concat([ 62 | new webpack.HotModuleReplacementPlugin(), 63 | new webpack.NoErrorsPlugin(), 64 | ]), 65 | devServer: { 66 | publicPath: devProps.baseUrl + '/assets/', 67 | host: devProps.host, 68 | hot: true, 69 | historyApiFallback: true, 70 | contentBase: devProps.contentBase, 71 | port: devProps.webpackPort, 72 | } 73 | }); 74 | -------------------------------------------------------------------------------- /webpack/webpack.config.client.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | context: __dirname, 6 | entry: [ 7 | 'regenerator/runtime', 8 | '../app/client/index', 9 | ], 10 | output: { 11 | path: path.join(__dirname, 'assets'), 12 | filename: 'client.bundle.js', 13 | publicPath: '/assets/', 14 | }, 15 | resolve: { 16 | extensions: ['', '.ts', '.tsx', '.js', '.jsx'], 17 | root: path.join(__dirname, '../app'), 18 | }, 19 | module: { 20 | loaders: [ 21 | { 22 | test: /\.tsx?$/, 23 | loader: 'babel!ts-loader', 24 | }, 25 | { 26 | test: /\.jsx?$/, 27 | loader: 'babel', 28 | exclude: /node_modules|lib/, 29 | }, 30 | { 31 | test: /\.css$/, 32 | loader: 'style-loader!css-loader' 33 | }, 34 | ], 35 | }, 36 | plugins: [ 37 | new webpack.PrefetchPlugin("react"), 38 | new webpack.PrefetchPlugin("react/lib/ReactComponentBrowserEnvironment") 39 | ] 40 | }; 41 | -------------------------------------------------------------------------------- /webpack/webpack.config.client.prod.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var config = require('./webpack.config.client'); 3 | var _ = require('lodash'); 4 | 5 | var config = module.exports = _.assign(_.clone(config), { 6 | plugins: (config.plugins || []).concat([ 7 | new webpack.DefinePlugin({ 8 | 'process.env.NODE_ENV': JSON.stringify('production'), 9 | }), 10 | new webpack.NoErrorsPlugin(), 11 | new webpack.optimize.DedupePlugin(), 12 | new webpack.optimize.UglifyJsPlugin({ 13 | compress: { warnings: false } 14 | }), 15 | new webpack.optimize.AggressiveMergingPlugin(), 16 | new webpack.optimize.OccurenceOrderPlugin(true), 17 | ]), 18 | }); -------------------------------------------------------------------------------- /webpack/webpack.config.server.dev.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var config = require('./webpack.config.server'); 3 | var _ = require('lodash'); 4 | 5 | var config = module.exports = _.assign(_.clone(config), { 6 | devtool: 'source-map', 7 | output: _.assign(_.clone(config.output), { 8 | pathinfo: true, 9 | }), 10 | }); 11 | -------------------------------------------------------------------------------- /webpack/webpack.config.server.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | 5 | var babelQuery = { 6 | cacheDirectory: true, 7 | 8 | presets: ['es2015'] 9 | }; 10 | 11 | 12 | module.exports = { 13 | context: __dirname, 14 | target: 'node', 15 | entry: [ 16 | 'regenerator/runtime', 17 | '../app/server/index', 18 | ], 19 | output: { 20 | path: path.join(__dirname, 'assets'), 21 | filename: 'server.bundle.js', 22 | publicPath: '/assets/', 23 | }, 24 | resolve: { 25 | extensions: ['', '.ts', '.tsx', '.js', '.jsx'], 26 | root: path.join(__dirname, '../app'), 27 | }, 28 | module: { 29 | loaders: [ 30 | { 31 | test: /\.tsx?$/, 32 | loader: 'babel?' + JSON.stringify(babelQuery) + '!ts-loader', 33 | exclude: /node_modules|lib/, 34 | }, 35 | { 36 | test: /\.jsx?$/, 37 | loader: 'babel?' + JSON.stringify(babelQuery), 38 | exclude: /node_modules|lib/, 39 | }, 40 | { 41 | test: /\.css$/, 42 | loader: 'style-loader!css-loader' 43 | }, 44 | ], 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /webpack/webpack.config.server.prod.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var config = require('./webpack.config.server'); 3 | var _ = require('lodash'); 4 | 5 | var config = module.exports = _.assign(_.clone(config), { 6 | plugins: (config.plugins || []).concat([ 7 | new webpack.DefinePlugin({ 8 | 'process.env.NODE_ENV': JSON.stringify('production'), 9 | }), 10 | new webpack.NoErrorsPlugin(), 11 | new webpack.optimize.DedupePlugin(), 12 | new webpack.optimize.UglifyJsPlugin({ 13 | compress: { warnings: false } 14 | }), 15 | new webpack.optimize.AggressiveMergingPlugin(), 16 | new webpack.optimize.OccurenceOrderPlugin(true), 17 | ]), 18 | }); --------------------------------------------------------------------------------