From 9cdd604137a6d316aadf45b33ef3c05b60765a8d Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 15 Mar 2016 21:02:54 +0900 Subject: [PATCH 01/46] Add .nvmrc file --- .nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..47e5d40a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v5 From 1f78eacd78fba98bf8eb203076c37b67b9784150 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 15 Mar 2016 21:18:10 +0900 Subject: [PATCH 02/46] Init --- package.json | 2 +- public/index.html | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index e7491981..52c4d28f 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,6 @@ }, "homepage": "https://github.com/reactjs/react-tutorial", "engines" : { - "node" : "0.12.x" + "node" : "5.x.x" } } diff --git a/public/index.html b/public/index.html index 21340e72..380b5081 100644 --- a/public/index.html +++ b/public/index.html @@ -13,7 +13,6 @@
- + diff --git a/public/scripts/tutorial1.js b/public/scripts/tutorial1.js new file mode 100644 index 00000000..05212235 --- /dev/null +++ b/public/scripts/tutorial1.js @@ -0,0 +1,14 @@ +var CommentBox = React.createClass({ + render: function() { + return ( +
+ Hello, world! I am a CommentBox. +
+ ); + } +}); + +ReactDOM.render( + , + document.getElementById('content') +); From 405487a651017ea4edb897340318c969ec5668a1 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 15 Mar 2016 21:33:41 +0900 Subject: [PATCH 04/46] =?UTF-8?q?html=20=EC=97=90=20=EA=B7=B8=EB=83=A5=20?= =?UTF-8?q?=EC=93=B0=EA=B8=B0=EB=A1=9C=20=ED=95=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 17 ++++++++++++++++- public/scripts/tutorial1.js | 14 -------------- 2 files changed, 16 insertions(+), 15 deletions(-) delete mode 100644 public/scripts/tutorial1.js diff --git a/public/index.html b/public/index.html index 50a40db9..1b1332ae 100644 --- a/public/index.html +++ b/public/index.html @@ -13,6 +13,21 @@
- + diff --git a/public/scripts/tutorial1.js b/public/scripts/tutorial1.js deleted file mode 100644 index 05212235..00000000 --- a/public/scripts/tutorial1.js +++ /dev/null @@ -1,14 +0,0 @@ -var CommentBox = React.createClass({ - render: function() { - return ( -
- Hello, world! I am a CommentBox. -
- ); - } -}); - -ReactDOM.render( - , - document.getElementById('content') -); From 21117d00577020cab2b186aaddba76d00100f311 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 15 Mar 2016 21:34:50 +0900 Subject: [PATCH 05/46] Add CommentList, CommentForm class --- public/index.html | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/public/index.html b/public/index.html index 1b1332ae..23e8fd47 100644 --- a/public/index.html +++ b/public/index.html @@ -14,20 +14,39 @@
From fc9dd566d8e26fcb3ced3314820c799955562ecd Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 15 Mar 2016 21:37:30 +0900 Subject: [PATCH 06/46] Render CommentBox with react components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CommentList 와 CommentForm 을 이용해서 CommentBox를 그림. - 이런 식으로 module 화 하면 되는구나. --- public/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 23e8fd47..9fa3ba26 100644 --- a/public/index.html +++ b/public/index.html @@ -38,7 +38,9 @@ render: function() { return (
- Hello, world! I am a CommentBox. +

Comments

+ +
); } From 02005d57701539072a7748c17762610994ef4a5c Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 15 Mar 2016 21:44:43 +0900 Subject: [PATCH 07/46] Minor syntax fix --- public/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/index.html b/public/index.html index 9fa3ba26..360893e4 100644 --- a/public/index.html +++ b/public/index.html @@ -18,7 +18,7 @@ render: function() { return (
- Hello, world! I am a CommentList. + Hello, world! I am a CommentList.
); } @@ -28,7 +28,7 @@ render: function() { return (
- Hello, world! I am a CommentForm. + Hello, world! I am a CommentForm.
); } From 783a574e7a70970eb0df86e1c643cc254309183d Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 15 Mar 2016 21:46:55 +0900 Subject: [PATCH 08/46] Add Comment component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - {}로 감싼 부분은 javascript expression 이다. - Component 별 variables 는 this.props 로 접근할 수 있다. --- public/index.html | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/public/index.html b/public/index.html index 360893e4..8e293ef3 100644 --- a/public/index.html +++ b/public/index.html @@ -14,6 +14,19 @@
From 47a521630307ffb3086257f626090cabb7a333f8 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Wed, 16 Mar 2016 11:51:22 +0900 Subject: [PATCH 13/46] =?UTF-8?q?Component=20=EC=97=90=20state=20=EB=8F=84?= =?UTF-8?q?=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - props 는 immutable 이다. - props 는 parent 가 child 로 전달하고, parent 가 소유한다. - component 에는 state 라는 private value 가 있다. (mutable) - this.setState() 를 통해서 state 가 update 되면, component 는 스스로를 다시 그린다. - genInitialState() 는 component 생애에서 한번 실행된다. --- public/index.html | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/public/index.html b/public/index.html index 6f9ba878..4846a8e3 100644 --- a/public/index.html +++ b/public/index.html @@ -66,18 +66,21 @@

}); var CommentBox = React.createClass({ + getInitialState: function() { + return {data: []}; + }, render: function() { return (

Comments

- +
); } }); ReactDOM.render( - , + , document.getElementById('content') ); From 349ec5228512b27be2f2e17fd6ae1a2531823fa4 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Wed, 16 Mar 2016 13:21:46 +0900 Subject: [PATCH 14/46] Add ajax call to server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - componentDidMount 는 component 가 그려지면 자동으로 실행된다. - this.setState 를 실행하면 component 가 자동으로 다시 그려진다. --- public/index.html | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/public/index.html b/public/index.html index 4846a8e3..75a3d5a6 100644 --- a/public/index.html +++ b/public/index.html @@ -69,6 +69,21 @@

getInitialState: function() { return {data: []}; }, + + componentDidMount: function() { + $.ajax({ + url: this.props.url, + dataType: 'json', + cache: false, + success: function(data) { + this.setState({data: data}); + }.bind(this), + error: function(xhr, status, err) { + console.error(this.props.url, status, err.toString()); + }.bind(this) + }); + }, + render: function() { return (
From 3c5e44193606f82e26fb85ee260e96d6fecf57d4 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Wed, 16 Mar 2016 13:33:42 +0900 Subject: [PATCH 15/46] =?UTF-8?q?2=EC=B4=88=EC=9D=98=20=EA=B0=84=EA=B2=A9?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9E=90=EB=8F=99=20load=20=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 간단하다. - 좋으네. --- public/index.html | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/public/index.html b/public/index.html index 75a3d5a6..60d5f501 100644 --- a/public/index.html +++ b/public/index.html @@ -66,11 +66,7 @@

}); var CommentBox = React.createClass({ - getInitialState: function() { - return {data: []}; - }, - - componentDidMount: function() { + loadCommentsFromServer: function() { $.ajax({ url: this.props.url, dataType: 'json', @@ -84,6 +80,15 @@

}); }, + getInitialState: function() { + return {data: []}; + }, + + componentDidMount: function() { + this.loadCommentsFromServer(); + setInterval(this.loadCommentsFromServer, this.props.pollInterval); + }, + render: function() { return (
@@ -95,7 +100,7 @@

Comments

} }); ReactDOM.render( - , + , document.getElementById('content') ); From 5b7224172f5c27177a09123db7bbb328b74ad176 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Wed, 16 Mar 2016 15:52:39 +0900 Subject: [PATCH 16/46] Add CommentForm - with basic html elements --- public/index.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/public/index.html b/public/index.html index 60d5f501..d7d0df04 100644 --- a/public/index.html +++ b/public/index.html @@ -58,9 +58,11 @@

var CommentForm = React.createClass({ render: function() { return ( -
- Hello, world! I am a CommentForm. -
+
+ + + +
); } }); From 05e0bf988030a7a910547fc24affda05e5b54b9b Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Wed, 16 Mar 2016 16:06:25 +0900 Subject: [PATCH 17/46] Controlled components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일반적으로 input element 는 browser가 생명주기를 결정함. - 이를 react 가 control 할 수 있도록 하는 방법. - onChange -> setState -> state 반영(value) --- public/index.html | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/public/index.html b/public/index.html index d7d0df04..f8dc8fc4 100644 --- a/public/index.html +++ b/public/index.html @@ -56,11 +56,30 @@

}); var CommentForm = React.createClass({ + getInitialState: function() { + return {author: '', text: ''}; + }, + handleAuthorChange: function(e) { + this.setState({author: e.target.value}); + }, + handleTextChange: function(e) { + this.setState({text: e.target.value}); + }, render: function() { return (
- - + +
); From 04ace90a10589d3b816234c04e294075a56ea68e Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Wed, 16 Mar 2016 16:10:27 +0900 Subject: [PATCH 18/46] Submitting the form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - form 의 생명주기 역시 react 로 가져옴. - e.preventDefault() 는 browser 의 기본 동작을 막는 것이다. --- public/index.html | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index f8dc8fc4..3f0eacb0 100644 --- a/public/index.html +++ b/public/index.html @@ -65,9 +65,19 @@

handleTextChange: function(e) { this.setState({text: e.target.value}); }, + handleSubmit: function(e) { + e.preventDefault(); + var author = this.state.author.trim(); + var text = this.state.text.trim(); + if (!text || !author) { + return; + } + // TODO: send request to the server + this.setState({author: '', text: ''}); + }, render: function() { return ( -
+ Date: Wed, 16 Mar 2016 16:28:57 +0900 Subject: [PATCH 19/46] Add callback from parent to child MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - props 를 사용함. - child 는 자신의 view 내에서 처리해야할 일만 처리하고, 다른 부분의 update 가 필요하다면 이를 parent 로 넘긴다. - parent 는 props 로 정의된 callback 에서 child 로부터 필요한 자료를 받아 전체 view 를 update 한다. --- comments.json | 7 ++++++- public/index.html | 20 ++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/comments.json b/comments.json index 7bef77ad..47789d7b 100644 --- a/comments.json +++ b/comments.json @@ -8,5 +8,10 @@ "id": 1420070400000, "author": "Paul O’Shannessy", "text": "React is *great*!" + }, + { + "id": 1458113230477, + "author": "Johnwook", + "text": "great!" } -] +] \ No newline at end of file diff --git a/public/index.html b/public/index.html index 3f0eacb0..62553837 100644 --- a/public/index.html +++ b/public/index.html @@ -73,6 +73,7 @@

return; } // TODO: send request to the server + this.props.onCommentSubmit({author: author, text: text}); this.setState({author: '', text: ''}); }, render: function() { @@ -110,22 +111,33 @@

}.bind(this) }); }, - + handleCommentSubmit: function(comment) { + $.ajax({ + url: this.props.url, + dataType: 'json', + type: 'POST', + data: comment, + success: function(data) { + this.setState({data: data}); + }.bind(this), + error: function(xhr, status, err) { + console.error(this.props.url, status, err.toString()); + }.bind(this) + }); + }, getInitialState: function() { return {data: []}; }, - componentDidMount: function() { this.loadCommentsFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); }, - render: function() { return (

Comments

- +
); } From 24eb11833f3a87fe1da2199ce8baee4e4cbab7d9 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Wed, 16 Mar 2016 16:33:54 +0900 Subject: [PATCH 20/46] Optimistic updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - view 를 바로 update 함으로써 속도를 더 빠른 것처럼 보이게 함. - request 가 요청됐으면 그릴 수도 있겠다. -> async backend! - 이런식으로 처리하면 실패시 대처를 잘해야겠다. --- comments.json | 7 +------ public/index.html | 8 ++++++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/comments.json b/comments.json index 47789d7b..7bef77ad 100644 --- a/comments.json +++ b/comments.json @@ -8,10 +8,5 @@ "id": 1420070400000, "author": "Paul O’Shannessy", "text": "React is *great*!" - }, - { - "id": 1458113230477, - "author": "Johnwook", - "text": "great!" } -] \ No newline at end of file +] diff --git a/public/index.html b/public/index.html index 62553837..b3eba8ab 100644 --- a/public/index.html +++ b/public/index.html @@ -112,6 +112,14 @@

}); }, handleCommentSubmit: function(comment) { + var comments = this.state.data; + // Optimistically set an id on the new comment. It will be replaced + // by an id generated by the server. In a production application you + // would likely not use Date.now() for this and would have a more + // robust system in place. + comment.id = Date.now(); + var newComments = comments.concat([comment]); + this.setState({data: newComments}); $.ajax({ url: this.props.url, dataType: 'json', From 2b51065a9889194e6aa930aab98a5d2ce2e4662b Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 22 Mar 2016 17:18:06 +0900 Subject: [PATCH 21/46] Install webpack --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 52c4d28f..212fdf1d 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "body-parser": "^1.4.3", "express": "^4.4.5" }, - "devDependencies": {}, + "devDependencies": { + "webpack": "^1.12.14" + }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" @@ -27,7 +29,7 @@ "url": "https://github.com/reactjs/react-tutorial/issues" }, "homepage": "https://github.com/reactjs/react-tutorial", - "engines" : { - "node" : "5.x.x" + "engines": { + "node": "5.x.x" } } From edbae771b98f7c78c4ff2b12c25f06191b034ec8 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 22 Mar 2016 17:30:41 +0900 Subject: [PATCH 22/46] ignore comments.json --- .gitignore | 1 + comments.json | 12 ------------ 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 comments.json diff --git a/.gitignore b/.gitignore index daeba5f9..ea751f3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *~ node_modules .DS_Store +comments.json diff --git a/comments.json b/comments.json deleted file mode 100644 index 7bef77ad..00000000 --- a/comments.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "id": 1388534400000, - "author": "Pete Hunt", - "text": "Hey there!" - }, - { - "id": 1420070400000, - "author": "Paul O’Shannessy", - "text": "React is *great*!" - } -] From 7a7b2b32835b14e42307ea6533532d10caeac401 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 22 Mar 2016 17:31:38 +0900 Subject: [PATCH 23/46] Remove data constants --- public/index.html | 5 ----- 1 file changed, 5 deletions(-) diff --git a/public/index.html b/public/index.html index b3eba8ab..e1a1b72e 100644 --- a/public/index.html +++ b/public/index.html @@ -14,11 +14,6 @@
+ - - - -
- From 3e9a8a5aa562446ec21f453e6c75d6bcf30675c1 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Mon, 11 Apr 2016 21:10:17 +0900 Subject: [PATCH 32/46] Make git ignore webpack bundled file --- .gitignore | 1 + public/dist/bundle.js | 206 ------------------------------------------ 2 files changed, 1 insertion(+), 206 deletions(-) delete mode 100644 public/dist/bundle.js diff --git a/.gitignore b/.gitignore index ea751f3e..323251e5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules .DS_Store comments.json +public/dist/bundle.js diff --git a/public/dist/bundle.js b/public/dist/bundle.js deleted file mode 100644 index 0c09bee6..00000000 --- a/public/dist/bundle.js +++ /dev/null @@ -1,206 +0,0 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports) { - - /** - * This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - - var Comment = React.createClass({ - displayName: "Comment", - - rawMarkup: function () { - var rawMarkup = marked(this.props.children.toString(), { sanitize: true }); - return { __html: rawMarkup }; - }, - - render: function () { - return React.createElement( - "div", - { className: "comment" }, - React.createElement( - "h2", - { className: "commentAuthor" }, - this.props.author - ), - React.createElement("span", { dangerouslySetInnerHTML: this.rawMarkup() }) - ); - } - }); - - var CommentBox = React.createClass({ - displayName: "CommentBox", - - loadCommentsFromServer: function () { - $.ajax({ - url: this.props.url, - dataType: 'json', - cache: false, - success: function (data) { - this.setState({ data: data }); - }.bind(this), - error: function (xhr, status, err) { - console.error(this.props.url, status, err.toString()); - }.bind(this) - }); - }, - handleCommentSubmit: function (comment) { - var comments = this.state.data; - // Optimistically set an id on the new comment. It will be replaced by an - // id generated by the server. In a production application you would likely - // not use Date.now() for this and would have a more robust system in place. - comment.id = Date.now(); - var newComments = comments.concat([comment]); - this.setState({ data: newComments }); - $.ajax({ - url: this.props.url, - dataType: 'json', - type: 'POST', - data: comment, - success: function (data) { - this.setState({ data: data }); - }.bind(this), - error: function (xhr, status, err) { - this.setState({ data: comments }); - console.error(this.props.url, status, err.toString()); - }.bind(this) - }); - }, - getInitialState: function () { - return { data: [] }; - }, - componentDidMount: function () { - this.loadCommentsFromServer(); - setInterval(this.loadCommentsFromServer, this.props.pollInterval); - }, - render: function () { - return React.createElement( - "div", - { className: "commentBox" }, - React.createElement( - "h1", - null, - "Comments" - ), - React.createElement(CommentList, { data: this.state.data }), - React.createElement(CommentForm, { onCommentSubmit: this.handleCommentSubmit }) - ); - } - }); - - var CommentList = React.createClass({ - displayName: "CommentList", - - render: function () { - var commentNodes = this.props.data.map(function (comment) { - return React.createElement( - Comment, - { author: comment.author, key: comment.id }, - comment.text - ); - }); - return React.createElement( - "div", - { className: "commentList" }, - commentNodes - ); - } - }); - - var CommentForm = React.createClass({ - displayName: "CommentForm", - - getInitialState: function () { - return { author: '', text: '' }; - }, - handleAuthorChange: function (e) { - this.setState({ author: e.target.value }); - }, - handleTextChange: function (e) { - this.setState({ text: e.target.value }); - }, - handleSubmit: function (e) { - e.preventDefault(); - var author = this.state.author.trim(); - var text = this.state.text.trim(); - if (!text || !author) { - return; - } - this.props.onCommentSubmit({ author: author, text: text }); - this.setState({ author: '', text: '' }); - }, - render: function () { - return React.createElement( - "form", - { className: "commentForm", onSubmit: this.handleSubmit }, - React.createElement("input", { - type: "text", - placeholder: "Your name", - value: this.state.author, - onChange: this.handleAuthorChange - }), - React.createElement("input", { - type: "text", - placeholder: "Say something...", - value: this.state.text, - onChange: this.handleTextChange - }), - React.createElement("input", { type: "submit", value: "Post" }) - ); - } - }); - - ReactDOM.render(React.createElement(CommentBox, { url: "/api/comments", pollInterval: 2000 }), document.getElementById('content')); - -/***/ } -/******/ ]); \ No newline at end of file From 6ea8086ae25c66fbe8f93d68028e871faca2a972 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 14 Apr 2016 14:05:15 +0900 Subject: [PATCH 33/46] Install package for es2015 - add configuration for it. --- package.json | 1 + webpack.config.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 49c58ade..f197c417 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "devDependencies": { "babel-core": "^6.7.6", "babel-loader": "^6.2.4", + "babel-preset-es2015": "^6.6.0", "babel-preset-react": "^6.5.0", "webpack": "^1.12.15" }, diff --git a/webpack.config.js b/webpack.config.js index efd2232c..527bc93f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -11,7 +11,7 @@ module.exports = { exclude: /(node_modules)/, loader: 'babel', query: { - presets: ['react'] + presets: ['es2015', 'react'] } } ] From e68f494b0d93fe7a7965b3f3d860601545bd69dc Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 14 Apr 2016 14:22:58 +0900 Subject: [PATCH 34/46] Add some ES2015 syntax - Say hello to ES2015 --- public/scripts/example.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/scripts/example.js b/public/scripts/example.js index 4e56a9c6..70f7f14c 100644 --- a/public/scripts/example.js +++ b/public/scripts/example.js @@ -9,10 +9,10 @@ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -var React = require('react'); -var ReactDOM = require('react-dom'); -var $ = require('jquery'); -var marked = require('marked'); +import React from 'react'; +import ReactDOM from 'react-dom'; +import $ from 'jquery'; +import marked from 'marked'; var Comment = React.createClass({ rawMarkup: function() { From fd4819aa8c2a49ac6bff8e52d697bf9867fc711f Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 14 Apr 2016 14:30:11 +0900 Subject: [PATCH 35/46] Remove comments.json from .gitignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - server 실행을 위해서는 comments 파일이 필요함 - 하지만 변화는 track 할 필요가 없음. - http://stackoverflow.com/questions/9794931/keep-file-in-a-git-repo-but-d ont-track-changes 참조 --- .gitignore | 1 - comments.json | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 comments.json diff --git a/.gitignore b/.gitignore index 323251e5..ea9a506d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ *~ node_modules .DS_Store -comments.json public/dist/bundle.js diff --git a/comments.json b/comments.json new file mode 100644 index 00000000..b5b90aed --- /dev/null +++ b/comments.json @@ -0,0 +1,17 @@ +[ + { + "id": 1388534400000, + "author": "Pete Hunt", + "text": "Hey there!" + }, + { + "id": 1420070400000, + "author": "Paul O’Shannessy", + "text": "React is *great*!" + }, + { + "id": 1458637037232, + "author": "ahoeu", + "text": "aoehuaoeu" + } +] \ No newline at end of file From 507d45b208228fe7289caa059b7c20074d88546b Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 21 Apr 2016 10:12:16 +0900 Subject: [PATCH 36/46] Update package dependency - there are always updates for react --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f197c417..51304c7f 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,19 @@ "description": "Code from the React tutorial.", "main": "server.js", "dependencies": { - "body-parser": "^1.15.0", + "body-parser": "^1.15.1", "express": "^4.13.4", "jquery": "^2.2.3", "marked": "^0.3.5", - "react": "^15.0.1", - "react-dom": "^15.0.1" + "react": "^15.0.2", + "react-dom": "^15.0.2" }, "devDependencies": { - "babel-core": "^6.7.6", + "babel-core": "^6.8.0", "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "babel-preset-react": "^6.5.0", - "webpack": "^1.12.15" + "webpack": "^1.13.0" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", From acc34ab97ae1af2693e4496df12a1b64f7d405e8 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 12 May 2016 10:57:58 +0900 Subject: [PATCH 37/46] Install eslint-airbnb-config - see https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb --- package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/package.json b/package.json index 51304c7f..b2db4bc7 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,11 @@ "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "babel-preset-react": "^6.5.0", + "eslint": "^2.9.0", + "eslint-config-airbnb": "^9.0.1", + "eslint-plugin-import": "^1.8.0", + "eslint-plugin-jsx-a11y": "^1.2.0", + "eslint-plugin-react": "^5.1.1", "webpack": "^1.13.0" }, "scripts": { From b8bcf26581411a07e41e0b5626451ecf1e655ac7 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 12 May 2016 11:01:17 +0900 Subject: [PATCH 38/46] Add eslintrc --- .eslintrc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .eslintrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..b0c0c8bd --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "airbnb" +} From 148c53899427ff469feef9bdfaf6477a85e5a95d Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 12 May 2016 14:16:11 +0900 Subject: [PATCH 39/46] Apply style guide to Comment component - see https://github.com/airbnb/javascript/tree/master/react for details --- public/scripts/Comment.jsx | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 public/scripts/Comment.jsx diff --git a/public/scripts/Comment.jsx b/public/scripts/Comment.jsx new file mode 100644 index 00000000..d60f6995 --- /dev/null +++ b/public/scripts/Comment.jsx @@ -0,0 +1,38 @@ +import React, { + Component, + PropTypes, +} from 'react'; +import marked from 'marked'; + +const propTypes = { + author: PropTypes.string, + children: PropTypes.object, +}; + +class Comment extends Component { + constructor(props) { + super(props); + + this.rawMarkup = this.rawMarkup.bind(this); + } + + rawMarkup() { + const rawMarkup = marked(this.props.children.toString(), { sanitize: true }); + return { __html: rawMarkup }; + } + + render() { + return ( +
+

+ {this.props.author} +

+ +
+ ); + } +} + +Comment.propTypes = propTypes; + +export default Comment; From de2c7800bc94f37247db1adde40d22a22299cdfa Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 12 May 2016 14:21:58 +0900 Subject: [PATCH 40/46] Apply style guide to CommentList component --- public/scripts/CommentList.jsx | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 public/scripts/CommentList.jsx diff --git a/public/scripts/CommentList.jsx b/public/scripts/CommentList.jsx new file mode 100644 index 00000000..a5f9afee --- /dev/null +++ b/public/scripts/CommentList.jsx @@ -0,0 +1,27 @@ +import React, { + PropTypes, +} from 'react'; + +import Comment from './Comment.jsx'; + +const propTypes = { + data: PropTypes.array, +}; + +function CommentList({ data }) { + return ( +
+ { + data.map(comment => + + {comment.text} + + ) + } +
+ ); +} + +CommentList.propTypes = propTypes; + +export default CommentList; From d468804b96a1e1243716412ad2bac40590567b97 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 12 May 2016 14:32:42 +0900 Subject: [PATCH 41/46] Apply style guide to CommentBox component --- public/scripts/CommentForm.jsx | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 public/scripts/CommentForm.jsx diff --git a/public/scripts/CommentForm.jsx b/public/scripts/CommentForm.jsx new file mode 100644 index 00000000..50ee19af --- /dev/null +++ b/public/scripts/CommentForm.jsx @@ -0,0 +1,57 @@ +import React, { + Component, + PropTypes, +} from 'react'; + +const propTypes = { + onCommentSubmit: PropTypes.func, +}; + +class CommentForm extends Component { + constructor(props) { + super(props); + this.state = { author: '', text: '' }; + this.handleAuthorChange = this.handleAuthorChange.bind(this); + this.handleTextChange = this.handleTextChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + handleAuthorChange(e) { + this.setState({ author: e.target.value }); + } + handleTextChange(e) { + this.setState({ text: e.target.value }); + } + handleSubmit(e) { + e.preventDefault(); + const author = this.state.author.trim(); + const text = this.state.text.trim(); + if (!text || !author) { + return; + } + this.props.onCommentSubmit({ author, text }); + this.setState({ author: '', text: '' }); + } + render() { + return ( + + + + + + ); + } +} + +CommentForm.propTypes = propTypes; + +export default CommentForm; From 36a28d1b851b772055604afe307e6816536dc3fc Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Thu, 12 May 2016 14:46:12 +0900 Subject: [PATCH 42/46] Apply style guide to CommentBox component --- public/scripts/CommentBox.jsx | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 public/scripts/CommentBox.jsx diff --git a/public/scripts/CommentBox.jsx b/public/scripts/CommentBox.jsx new file mode 100644 index 00000000..057be25f --- /dev/null +++ b/public/scripts/CommentBox.jsx @@ -0,0 +1,73 @@ +import React, { + Component, + PropTypes, +} from 'react'; +import $ from 'jquery'; + +import CommentList from './Comment.jsx'; +import CommentForm from './CommentForm.jsx'; + +const propTypes = { + url: PropTypes.string, + pollInterval: PropTypes.number, +}; + +class CommentBox extends Component { + constructor(props) { + super(props); + this.state = { data: [] }; + this.loadCommentsFromServer = this.loadCommentsFromServer.bind(this); + } + componentDidMount() { + this.loadCommentsFromServer(); + setInterval(this.loadCommentsFromServer, this.props.pollInterval); + } + loadCommentsFromServer() { + $.ajax({ + url: this.props.url, + dataType: 'json', + cache: false, + success(data) { + this.setState({ data }); + }, + error(xhr, status, err) { + console.error(this.props.url, status, err.toString()); + }, + }); + } + handleCommentSubmit(comment) { + const comments = this.state.data; + // Optimistically set an id on the new comment. It will be replaced by an + // id generated by the server. In a production application you would likely + // not use Date.now() for this and would have a more robust system in place. + comment.id = Date.now(); + const newComments = comments.concat([comment]); + this.setState({ data: newComments }); + $.ajax({ + url: this.props.url, + dataType: 'json', + type: 'POST', + data: comment, + success(data) { + this.setState({ data }); + }, + error(xhr, status, err) { + this.setState({ data: comments }); + console.error(this.props.url, status, err.toString()); + }, + }); + } + render() { + return ( +
+

Comments

+ + +
+ ); + } +} + +CommentBox.propTypes = propTypes; + +export default CommentBox; From 85eaf3d53a1fb2e4bcb6ce093bf6487bb3b2eb1d Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Mon, 16 May 2016 17:17:45 +0900 Subject: [PATCH 43/46] Fix invalid proptypes, remove jquery dependency - jquery is just for make it work. - To just focus on react itself, remove it --- package.json | 3 +- public/scripts/Comment.jsx | 2 +- public/scripts/CommentBox.jsx | 53 +++++------- public/scripts/CommentForm.jsx | 2 +- public/scripts/example.js | 143 +-------------------------------- 5 files changed, 25 insertions(+), 178 deletions(-) diff --git a/package.json b/package.json index b2db4bc7..2487a7e6 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "dependencies": { "body-parser": "^1.15.1", "express": "^4.13.4", - "jquery": "^2.2.3", "marked": "^0.3.5", "react": "^15.0.2", "react-dom": "^15.0.2" @@ -16,7 +15,7 @@ "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "babel-preset-react": "^6.5.0", - "eslint": "^2.9.0", + "eslint": "^2.10.1", "eslint-config-airbnb": "^9.0.1", "eslint-plugin-import": "^1.8.0", "eslint-plugin-jsx-a11y": "^1.2.0", diff --git a/public/scripts/Comment.jsx b/public/scripts/Comment.jsx index d60f6995..e4465783 100644 --- a/public/scripts/Comment.jsx +++ b/public/scripts/Comment.jsx @@ -6,7 +6,7 @@ import marked from 'marked'; const propTypes = { author: PropTypes.string, - children: PropTypes.object, + children: PropTypes.string, }; class Comment extends Component { diff --git a/public/scripts/CommentBox.jsx b/public/scripts/CommentBox.jsx index 057be25f..d48aa9f4 100644 --- a/public/scripts/CommentBox.jsx +++ b/public/scripts/CommentBox.jsx @@ -2,11 +2,28 @@ import React, { Component, PropTypes, } from 'react'; -import $ from 'jquery'; -import CommentList from './Comment.jsx'; +import CommentList from './CommentList.jsx'; import CommentForm from './CommentForm.jsx'; +const tempComments = [ + { + id: 1388534400000, + author: 'Pete Hunt', + text: 'Hey there!', + }, + { + id: 1420070400000, + author: 'Paul O’Shannessy', + text: 'React is *great*!', + }, + { + id: 1458637037232, + author: 'ahoeu', + text: 'aoehuaoeu', + }, +]; + const propTypes = { url: PropTypes.string, pollInterval: PropTypes.number, @@ -16,46 +33,18 @@ class CommentBox extends Component { constructor(props) { super(props); this.state = { data: [] }; - this.loadCommentsFromServer = this.loadCommentsFromServer.bind(this); + this.handleCommentSubmit = this.handleCommentSubmit.bind(this); } componentDidMount() { this.loadCommentsFromServer(); - setInterval(this.loadCommentsFromServer, this.props.pollInterval); } loadCommentsFromServer() { - $.ajax({ - url: this.props.url, - dataType: 'json', - cache: false, - success(data) { - this.setState({ data }); - }, - error(xhr, status, err) { - console.error(this.props.url, status, err.toString()); - }, - }); + this.setState({ data: tempComments }); } handleCommentSubmit(comment) { const comments = this.state.data; - // Optimistically set an id on the new comment. It will be replaced by an - // id generated by the server. In a production application you would likely - // not use Date.now() for this and would have a more robust system in place. - comment.id = Date.now(); const newComments = comments.concat([comment]); this.setState({ data: newComments }); - $.ajax({ - url: this.props.url, - dataType: 'json', - type: 'POST', - data: comment, - success(data) { - this.setState({ data }); - }, - error(xhr, status, err) { - this.setState({ data: comments }); - console.error(this.props.url, status, err.toString()); - }, - }); } render() { return ( diff --git a/public/scripts/CommentForm.jsx b/public/scripts/CommentForm.jsx index 50ee19af..43a30e0c 100644 --- a/public/scripts/CommentForm.jsx +++ b/public/scripts/CommentForm.jsx @@ -28,7 +28,7 @@ class CommentForm extends Component { if (!text || !author) { return; } - this.props.onCommentSubmit({ author, text }); + this.props.onCommentSubmit({ id: Date.now(), author, text }); this.setState({ author: '', text: '' }); } render() { diff --git a/public/scripts/example.js b/public/scripts/example.js index 70f7f14c..526c172f 100644 --- a/public/scripts/example.js +++ b/public/scripts/example.js @@ -1,148 +1,7 @@ -/** - * This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ import React from 'react'; import ReactDOM from 'react-dom'; -import $ from 'jquery'; -import marked from 'marked'; -var Comment = React.createClass({ - rawMarkup: function() { - var rawMarkup = marked(this.props.children.toString(), {sanitize: true}); - return { __html: rawMarkup }; - }, - - render: function() { - return ( -
-

- {this.props.author} -

- -
- ); - } -}); - -var CommentBox = React.createClass({ - loadCommentsFromServer: function() { - $.ajax({ - url: this.props.url, - dataType: 'json', - cache: false, - success: function(data) { - this.setState({data: data}); - }.bind(this), - error: function(xhr, status, err) { - console.error(this.props.url, status, err.toString()); - }.bind(this) - }); - }, - handleCommentSubmit: function(comment) { - var comments = this.state.data; - // Optimistically set an id on the new comment. It will be replaced by an - // id generated by the server. In a production application you would likely - // not use Date.now() for this and would have a more robust system in place. - comment.id = Date.now(); - var newComments = comments.concat([comment]); - this.setState({data: newComments}); - $.ajax({ - url: this.props.url, - dataType: 'json', - type: 'POST', - data: comment, - success: function(data) { - this.setState({data: data}); - }.bind(this), - error: function(xhr, status, err) { - this.setState({data: comments}); - console.error(this.props.url, status, err.toString()); - }.bind(this) - }); - }, - getInitialState: function() { - return {data: []}; - }, - componentDidMount: function() { - this.loadCommentsFromServer(); - setInterval(this.loadCommentsFromServer, this.props.pollInterval); - }, - render: function() { - return ( -
-

Comments

- - -
- ); - } -}); - -var CommentList = React.createClass({ - render: function() { - var commentNodes = this.props.data.map(function(comment) { - return ( - - {comment.text} - - ); - }); - return ( -
- {commentNodes} -
- ); - } -}); - -var CommentForm = React.createClass({ - getInitialState: function() { - return {author: '', text: ''}; - }, - handleAuthorChange: function(e) { - this.setState({author: e.target.value}); - }, - handleTextChange: function(e) { - this.setState({text: e.target.value}); - }, - handleSubmit: function(e) { - e.preventDefault(); - var author = this.state.author.trim(); - var text = this.state.text.trim(); - if (!text || !author) { - return; - } - this.props.onCommentSubmit({author: author, text: text}); - this.setState({author: '', text: ''}); - }, - render: function() { - return ( -
- - - -
- ); - } -}); +import CommentBox from './CommentBox.jsx'; ReactDOM.render( , From b30e7dbeba2087de35faf1bf2269e80031852a19 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Mon, 16 May 2016 18:12:29 +0900 Subject: [PATCH 44/46] Remove server related files --- package.json | 4 +- server.go | 112 --------------------------------------------------- server.js | 77 ----------------------------------- server.php | 53 ------------------------ server.pl | 38 ----------------- server.py | 36 ----------------- server.rb | 49 ---------------------- 7 files changed, 1 insertion(+), 368 deletions(-) delete mode 100644 server.go delete mode 100644 server.js delete mode 100644 server.php delete mode 100644 server.pl delete mode 100644 server.py delete mode 100644 server.rb diff --git a/package.json b/package.json index 2487a7e6..0af9f3f9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,6 @@ "name": "react-tutorial", "version": "0.0.0", "description": "Code from the React tutorial.", - "main": "server.js", "dependencies": { "body-parser": "^1.15.1", "express": "^4.13.4", @@ -23,8 +22,7 @@ "webpack": "^1.13.0" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node server.js" + "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", diff --git a/server.go b/server.go deleted file mode 100644 index 934a4cfc..00000000 --- a/server.go +++ /dev/null @@ -1,112 +0,0 @@ -/** - * This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "sync" - "time" -) - -type comment struct { - ID int64 `json:"id"` - Author string `json:"author"` - Text string `json:"text"` -} - -const dataFile = "./comments.json" - -var commentMutex = new(sync.Mutex) - -// Handle comments -func handleComments(w http.ResponseWriter, r *http.Request) { - // Since multiple requests could come in at once, ensure we have a lock - // around all file operations - commentMutex.Lock() - defer commentMutex.Unlock() - - // Stat the file, so we can find its current permissions - fi, err := os.Stat(dataFile) - if err != nil { - http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - // Read the comments from the file. - commentData, err := ioutil.ReadFile(dataFile) - if err != nil { - http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - switch r.Method { - case "POST": - // Decode the JSON data - var comments []comment - if err := json.Unmarshal(commentData, &comments); err != nil { - http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - // Add a new comment to the in memory slice of comments - comments = append(comments, comment{ID: time.Now().UnixNano() / 1000000, Author: r.FormValue("author"), Text: r.FormValue("text")}) - - // Marshal the comments to indented json. - commentData, err = json.MarshalIndent(comments, "", " ") - if err != nil { - http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError) - return - } - - // Write out the comments to the file, preserving permissions - err := ioutil.WriteFile(dataFile, commentData, fi.Mode()) - if err != nil { - http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Cache-Control", "no-cache") - w.Header().Set("Access-Control-Allow-Origin", "*") - io.Copy(w, bytes.NewReader(commentData)) - - case "GET": - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Cache-Control", "no-cache") - w.Header().Set("Access-Control-Allow-Origin", "*") - // stream the contents of the file to the response - io.Copy(w, bytes.NewReader(commentData)) - - default: - // Don't know the method, so error - http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed) - } -} - -func main() { - port := os.Getenv("PORT") - if port == "" { - port = "3000" - } - http.HandleFunc("/api/comments", handleComments) - http.Handle("/", http.FileServer(http.Dir("./public"))) - log.Println("Server started: http://localhost:" + port) - log.Fatal(http.ListenAndServe(":"+port, nil)) -} diff --git a/server.js b/server.js deleted file mode 100644 index b5a7218a..00000000 --- a/server.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * This file provided by Facebook is for non-commercial testing and evaluation - * purposes only. Facebook reserves all rights not expressly granted. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -var fs = require('fs'); -var path = require('path'); -var express = require('express'); -var bodyParser = require('body-parser'); -var app = express(); - -var COMMENTS_FILE = path.join(__dirname, 'comments.json'); - -app.set('port', (process.env.PORT || 3000)); - -app.use('/', express.static(path.join(__dirname, 'public'))); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({extended: true})); - -// Additional middleware which will set headers that we need on each request. -app.use(function(req, res, next) { - // Set permissive CORS header - this allows this server to be used only as - // an API server in conjunction with something like webpack-dev-server. - res.setHeader('Access-Control-Allow-Origin', '*'); - - // Disable caching so we'll always get the latest comments. - res.setHeader('Cache-Control', 'no-cache'); - next(); -}); - -app.get('/api/comments', function(req, res) { - fs.readFile(COMMENTS_FILE, function(err, data) { - if (err) { - console.error(err); - process.exit(1); - } - res.json(JSON.parse(data)); - }); -}); - -app.post('/api/comments', function(req, res) { - fs.readFile(COMMENTS_FILE, function(err, data) { - if (err) { - console.error(err); - process.exit(1); - } - var comments = JSON.parse(data); - // NOTE: In a real implementation, we would likely rely on a database or - // some other approach (e.g. UUIDs) to ensure a globally unique id. We'll - // treat Date.now() as unique-enough for our purposes. - var newComment = { - id: Date.now(), - author: req.body.author, - text: req.body.text, - }; - comments.push(newComment); - fs.writeFile(COMMENTS_FILE, JSON.stringify(comments, null, 4), function(err) { - if (err) { - console.error(err); - process.exit(1); - } - res.json(comments); - }); - }); -}); - - -app.listen(app.get('port'), function() { - console.log('Server started: http://localhost:' + app.get('port') + '/'); -}); diff --git a/server.php b/server.php deleted file mode 100644 index 75fae215..00000000 --- a/server.php +++ /dev/null @@ -1,53 +0,0 @@ - round(microtime(true) * 1000), - 'author' => $_POST['author'], - 'text' => $_POST['text'] - ]; - - $comments = json_encode($commentsDecoded, JSON_PRETTY_PRINT); - file_put_contents('comments.json', $comments); - } - header('Content-Type: application/json'); - header('Cache-Control: no-cache'); - header('Access-Control-Allow-Origin: *'); - echo $comments; - } else { - return false; - } -} diff --git a/server.pl b/server.pl deleted file mode 100644 index c3212b9c..00000000 --- a/server.pl +++ /dev/null @@ -1,38 +0,0 @@ -# This file provided by Facebook is for non-commercial testing and evaluation -# purposes only. Facebook reserves all rights not expressly granted. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -use Time::HiRes qw(gettimeofday); -use Mojolicious::Lite; -use Mojo::JSON qw(encode_json decode_json); - -app->static->paths->[0] = './public'; - -any '/' => sub { $_[0]->reply->static('index.html') }; - -any [qw(GET POST)] => '/api/comments' => sub { - my $self = shift; - my $comments = decode_json (do { local(@ARGV,$/) = 'comments.json';<> }); - $self->res->headers->cache_control('no-cache'); - $self->res->headers->access_control_allow_origin('*'); - - if ($self->req->method eq 'POST') - { - push @$comments, { - id => int(gettimeofday * 1000), - author => $self->param('author'), - text => $self->param('text'), - }; - open my $FILE, '>', 'comments.json'; - print $FILE encode_json($comments); - } - $self->render(json => $comments); -}; -my $port = $ENV{PORT} || 3000; -app->start('daemon', '-l', "http://*:$port"); diff --git a/server.py b/server.py deleted file mode 100644 index 5cf598df..00000000 --- a/server.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file provided by Facebook is for non-commercial testing and evaluation -# purposes only. Facebook reserves all rights not expressly granted. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import json -import os -import time -from flask import Flask, Response, request - -app = Flask(__name__, static_url_path='', static_folder='public') -app.add_url_rule('/', 'root', lambda: app.send_static_file('index.html')) - -@app.route('/api/comments', methods=['GET', 'POST']) -def comments_handler(): - - with open('comments.json', 'r') as file: - comments = json.loads(file.read()) - - if request.method == 'POST': - newComment = request.form.to_dict() - newComment['id'] = int(time.time() * 1000) - comments.append(newComment) - - with open('comments.json', 'w') as file: - file.write(json.dumps(comments, indent=4, separators=(',', ': '))) - - return Response(json.dumps(comments), mimetype='application/json', headers={'Cache-Control': 'no-cache', 'Access-Control-Allow-Origin': '*'}) - -if __name__ == '__main__': - app.run(port=int(os.environ.get("PORT",3000))) diff --git a/server.rb b/server.rb deleted file mode 100644 index 698f4339..00000000 --- a/server.rb +++ /dev/null @@ -1,49 +0,0 @@ -# This file provided by Facebook is for non-commercial testing and evaluation -# purposes only. Facebook reserves all rights not expressly granted. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -require 'webrick' -require 'json' - -# default port to 3000 or overwrite with PORT variable by running -# $ PORT=3001 ruby server.rb -port = ENV['PORT'] ? ENV['PORT'].to_i : 3000 - -puts "Server started: http://localhost:#{port}/" - -root = File.expand_path './public' -server = WEBrick::HTTPServer.new Port: port, DocumentRoot: root - -server.mount_proc '/api/comments' do |req, res| - comments = JSON.parse(File.read('./comments.json', encoding: 'UTF-8')) - - if req.request_method == 'POST' - # Assume it's well formed - comment = { id: (Time.now.to_f * 1000).to_i } - req.query.each do |key, value| - comment[key] = value.force_encoding('UTF-8') unless key == 'id' - end - comments << comment - File.write( - './comments.json', - JSON.pretty_generate(comments, indent: ' '), - encoding: 'UTF-8' - ) - end - - # always return json - res['Content-Type'] = 'application/json' - res['Cache-Control'] = 'no-cache' - res['Access-Control-Allow-Origin'] = '*' - res.body = JSON.generate(comments) -end - -trap('INT') { server.shutdown } - -server.start From 0bf18d5bcdca5fc1533d2c98e2694954365f9d47 Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 17 May 2016 14:08:06 +0900 Subject: [PATCH 45/46] Update package dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0af9f3f9..fd859f8b 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "babel-preset-react": "^6.5.0", - "eslint": "^2.10.1", + "eslint": "^2.10.2", "eslint-config-airbnb": "^9.0.1", "eslint-plugin-import": "^1.8.0", "eslint-plugin-jsx-a11y": "^1.2.0", From 8ccd1c79cb5a4c6b6ba2d6d4bf9b5bd9dafe744c Mon Sep 17 00:00:00 2001 From: Johnwook Choi Date: Tue, 17 May 2016 18:14:20 +0900 Subject: [PATCH 46/46] Apply es2015 to webpack.config --- .babelrc | 3 +++ package.json | 1 + webpack.config.babel.js | 19 +++++++++++++++++++ webpack.config.js | 19 ------------------- 4 files changed, 23 insertions(+), 19 deletions(-) create mode 100644 .babelrc create mode 100644 webpack.config.babel.js delete mode 100644 webpack.config.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..c13c5f62 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015"] +} diff --git a/package.json b/package.json index fd859f8b..0475a60e 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "babel-preset-react": "^6.5.0", + "babel-register": "^6.8.0", "eslint": "^2.10.2", "eslint-config-airbnb": "^9.0.1", "eslint-plugin-import": "^1.8.0", diff --git a/webpack.config.babel.js b/webpack.config.babel.js new file mode 100644 index 00000000..f2035266 --- /dev/null +++ b/webpack.config.babel.js @@ -0,0 +1,19 @@ +export default { + entry: './public/scripts/example.js', + output: { + path: `${__dirname}/public/dist`, + filename: 'bundle.js', + }, + module: { + loaders: [ + { + test: /\.jsx?$/, + exclude: /(node_modules)/, + loader: 'babel', + query: { + presets: ['es2015', 'react'], + }, + }, + ], + }, +}; diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 527bc93f..00000000 --- a/webpack.config.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = { - entry: "./public/scripts/example.js", - output: { - path: __dirname + "/public/dist", - filename: "bundle.js" - }, - module: { - loaders: [ - { - test: /\.jsx?$/, - exclude: /(node_modules)/, - loader: 'babel', - query: { - presets: ['es2015', 'react'] - } - } - ] - } -};