Adding NPM, Babel, Webpack for basic React front end

This commit is contained in:
Alex Hunt 2017-11-03 22:00:05 -04:00
parent cfbaf067ed
commit a1748b7220
29 changed files with 178633 additions and 17 deletions

View File

@ -1,16 +1,21 @@
- v0.1 Basic Reader
- v0.1 Text Editor + Basic Reader
+ Flask Microservice +
+ Basic REST API +
+ Basic Layout +
- React Components
- Wire React to ES API
- Simple CRUID interface
- Orchestrate Environment Configs
- ElasticSearch Schema
- Reader Layout
- Bootstrap / React with Bower
- Basic Testing
- Front-End Dependency Manager
- v0.2 Chapter CRUD
- Chapter Manage Component with Create and Delete Options
- Edit Chapter Component with Delete
- v0.2 Reader
- Reader UX
- Top Bar
- Background Texture - Canvas / Paper
- v0.3 Notes
- v0.3 Notes and Text Formatting
- Abstract API Blueprint for Notes
- Note Manager Component
- Error Handling

View File

@ -1,13 +1,27 @@
from flask import Flask
from flask_webpack import Webpack
from werkzeug.serving import run_simple
from blueprints.reader import reader
from blueprints.api import api
# Initialize application
application = Flask(__name__)
params = {
'DEBUG': True,
'WEBPACK_MANIFEST_PATH': './static/js/manifest.json'
}
application.config.update(params)
webpack = Webpack()
webpack.init_app(application)
# Register blueprints
application.register_blueprint(reader)
application.register_blueprint(api, url_prefix='/api')
if __name__ == "__main__":
application.run()
# application.debug=True
run_simple('localhost', 5000, application, use_reloader=True, use_debugger=True)

View File

@ -1,26 +1,30 @@
from flask import Blueprint, render_template, abort, jsonify, request
from jinja2 import TemplateNotFound
from elasticsearch import Elasticsearch
#Elasticsearch local connection
#TODO: Extract to config
ELASTICSEARCH_HOST = '127.0.0.1:9200'
es = Elasticsearch(ELASTICSEARCH_HOST)
# if es.ping() == True?
print 'API initialized!'
api = Blueprint('api', __name__)
# Get all chapters
@api.route('/chapters')
@api.route('/chapters/')
def get_chapters():
res = es.search(index='joyce', doc_type='chapters', body={'query': {'match_all': {}}})
# chapters = { your_key: old_dict[your_key] for your_key in your_keys }
search = es.search(index='joyce', doc_type='chapters', body={'query': {'match_all': {}}})
res = []
for x in search['hits']['hits']:
res.append(x['_source'])
return jsonify(res)
# Get singular chapter
# Get specific chapter
@api.route('/chapters/<int:id>')
def get_chapter(id):
res = es.get(index='joyce', doc_type='chapters', id=id, _source=True)
return jsonify(res['_source'])
res = es.get_source(index='joyce', doc_type='chapters', id=id, _source=True)
return jsonify(res)
# Create and update chapters
@api.route('/chapters/<int:id>', methods=['POST', 'PUT'])

0
blueprints/editor.py Normal file
View File

30
package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "joyce",
"version": "0.0.1",
"description": "Reader and Editor for Hypertext",
"main": "app.js",
"scripts": {
"start": "webpack-dev-server --progress --colors --port 2992 --content-base static/js",
"build": "webpack --config webpack.config.js --watch",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Alex Hunt",
"license": "ISC",
"dependencies": {
"react": "^16.0.0",
"react-dom": "^16.0.0"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"clean-webpack-plugin": "^0.1.17",
"html-webpack-plugin": "^2.30.1",
"manifest-revision-webpack-plugin": "^0.4.1",
"sync-exec": "^0.6.2",
"webpack": "^3.8.1",
"webpack-dev-server": "^2.9.3",
"webpack-manifest-plugin": "^1.3.2"
}
}

View File

@ -6,6 +6,7 @@ dominate==2.3.1
elasticsearch==5.4.0
Flask==0.12.2
Flask-Bootstrap==3.3.7.1
Flask-Webpack==0.1.0
itsdangerous==0.24
Jinja2==2.9.6
macholib==1.5.1

48
src/app.js Normal file
View File

@ -0,0 +1,48 @@
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class ChapterButton extends React.Component {
}
class ChapterList extends React.Component {
constructor(props) {
super(props)
this.state = {
chapters: []
}
}
componentDidMount() {
const chapters = axios.get('/api/chapters').then(res => {
const chapters = res.data.sort((a, b) => a.id - b.id)
return chapters
}).catch(error =>
console.log(error)
).then(chapters => this.setState({chapters}))
}
render() {
return (
<div>
<div id="highlight_button" className="text-center">
<a href="#" className="btn btn-primary btn-lg">Highlight Notes</a>
</div>
{console.log(this.state.chapters)}
{this.state.chapters.map(chapter =>
<div>
<a className='btn btn-primary btn-lg' href={chapter.id}>{chapter.name}</a>
</div>
)}
</div>
);
}
}
ReactDOM.render(
<ChapterList chapter='Telemachus'/>,
document.getElementById('sidebar')
)

BIN
static/.DS_Store vendored Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,84 @@
/******/ (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] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = 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;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "static/js/";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 22);
/******/ })
/************************************************************************/
/******/ ({
/***/ 22:
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(23);
/***/ }),
/***/ 23:
/***/ (function(module, exports) {
"use strict";
throw new Error("Module build failed: SyntaxError: Unexpected token (26:27)\n\n\u001b[0m \u001b[90m 24 | \u001b[39m\n \u001b[90m 25 | \u001b[39m\tcomponentDidMount() {\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 26 | \u001b[39m\t\t\u001b[36mconst\u001b[39m chapters\u001b[33m:\u001b[39m axios\u001b[33m.\u001b[39mget(\u001b[32m'/api/chapters'\u001b[39m)\u001b[33m.\u001b[39mthen(res \u001b[33m=>\u001b[39m {\n \u001b[90m | \u001b[39m\t\t \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 27 | \u001b[39m\t\t\t\u001b[36mconst\u001b[39m chapters \u001b[33m=\u001b[39m res\u001b[33m.\u001b[39mdata\u001b[33m.\u001b[39msort((a\u001b[33m,\u001b[39m b) \u001b[33m=>\u001b[39m a\u001b[33m.\u001b[39mid \u001b[33m-\u001b[39m b\u001b[33m.\u001b[39mid)\n \u001b[90m 28 | \u001b[39m\t\t\t\u001b[90m// console.log(chapters)\u001b[39m\n \u001b[90m 29 | \u001b[39m\t\t\t\u001b[36mreturn\u001b[39m chapters\u001b[0m\n");
/***/ })
/******/ });

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
static/js/manifest.json Normal file
View File

@ -0,0 +1 @@
{"assets":{"app.js":"app.2803b3dead751fb6d8e3.js"},"publicPath":"static/js/"}

28
static/style.css Normal file
View File

@ -0,0 +1,28 @@
html body {
background-color: rgba(211,211,211,1);
}
#joyce_reader {
width: 95%;
margin-top: 1%;
}
#sidebar {
background-color: rgba(250,250,250,.8);
min-height: 300px;
padding: 2%;
border: 1px solid rgba(120,120,120,.6);
border-radius: 5px;
}
#reader {
background-color: rgba(250,250,250,.8);
min-height: 700px;
padding: 2%;
border: 1px solid rgba(120,120,120,.6);
}
#highlight_button {
padding: 5% 0 5% 0;
border-bottom: 2px solid rgba(40,40,40,.5);
}

View File

@ -1 +1,34 @@
<p>Hello world!</p>
<!doctype html>
<head>
<title>Joyce</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<link rel="manifest" href="static/js/manifest.json">
<!-- TODO: Swap CDN Links for Bower / Yarn -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<!-- Reader Layout -->
<div id="joyce_reader" class="container-fluid">
<div class="row">
<div class="col-sm-2" id="sidebar">
<div id="highlight_button" class="text-center">
<a href="#" class="btn btn-primary btn-lg">Highlight Notes</a>
</div>
<div>
List of Chapters!
</div>
</div>
<div class="col-sm-6" id="reader">
</div>
</div>
</div>
<!-- Scripts -->
<script src="{{asset_url_for('app.js')}}"></script>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>

47
webpack.config.js Normal file
View File

@ -0,0 +1,47 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin')
const ManifestRevisionPlugin = require('manifest-revision-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
const rootAssetPath = './src/';
let pathsToClean = [
'static/js/'
]
module.exports = {
entry: {
app: [
rootAssetPath + 'app'
]
},
output: {
publicPath: "static/js/",
filename: 'app.[chunkhash].js',
path: path.resolve(__dirname, 'static/js')
},
watch: true,
watchOptions: {
poll: true,
ignored: /node_modules/
},
module : {
rules: [{
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
}
}]
},
plugins: [
// TODO: Dev / Prod Config
// new webpack.optimize.UglifyJsPlugin(),
new ManifestRevisionPlugin(path.join('static/js', 'manifest.json'), {
rootAssetPath: rootAssetPath
}),
new CleanWebpackPlugin(pathsToClean)
],
};