@@ -18,19 +16,13 @@ class Welcome extends React.Component {
}
thingsToDo() {
-
- // things = for key, value of pkgman.invoke 'perseaThingsToDo'
- //
{value}
-
- // if things.length is 0
const things = You have nothing to do!
;
-
return
{things}
;
}
}
-// import {styler} from '@truss/react'
-// Welcome = styler Welcome, require './welcome.scss'
+import contempo from 'contempo';
+Welcome = contempo(Welcome, require('./welcome.scss'));
export default Welcome;
diff --git a/frontend/root/welcome.scss b/frontend/app/welcome.scss
similarity index 100%
rename from frontend/root/welcome.scss
rename to frontend/app/welcome.scss
diff --git a/frontend/index.html b/frontend/index.html
index de5139a..f30cb82 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,7 +2,7 @@
-
Truss
+
Persea
diff --git a/frontend/root/index.dev.js b/frontend/root/index.dev.js
index 35cd4c0..591db2e 100644
--- a/frontend/root/index.dev.js
+++ b/frontend/root/index.dev.js
@@ -4,7 +4,8 @@ import {BrowserRouter} from 'react-router-dom';
import DevTools from './dev-tools';
-import App from './app';
+import App from '../app';
+
function Root ({store}) {
return
@@ -19,7 +20,7 @@ function Root ({store}) {
;
};
-// import {styler} from '@truss/react'
-// Root = styler Root, require './index.scss'
+import contempo from 'contempo';
+Root = contempo(Root, require('./index.scss'));
export default Root;
diff --git a/frontend/root/index.js b/frontend/root/index.js
index 026a88d..f669d43 100644
--- a/frontend/root/index.js
+++ b/frontend/root/index.js
@@ -1,4 +1,3 @@
-
if ('production' === process.env.NODE_ENV) {
module.exports = require('./index.prod');
}
diff --git a/frontend/root/index.prod.js b/frontend/root/index.prod.js
index 13d5720..9dda690 100644
--- a/frontend/root/index.prod.js
+++ b/frontend/root/index.prod.js
@@ -2,7 +2,8 @@ import React from 'react';
import {Provider} from 'react-redux';
import {BrowserRouter} from 'react-router-dom';
-import App from './app';
+import App from '../app';
+
function Root ({store}) {
return
@@ -12,7 +13,7 @@ function Root ({store}) {
;
};
-// import {styler} from '@truss/react'
-// Root = styler Root, require './index.scss'
+import contempo from 'contempo';
+Root = contempo(Root, require('./index.scss'));
export default Root;
diff --git a/frontend/root/reducer.js b/frontend/root/reducer.js
index 64e9be1..8e5d6de 100644
--- a/frontend/root/reducer.js
+++ b/frontend/root/reducer.js
@@ -1,20 +1,9 @@
import {combineReducers} from 'redux';
import {reducer as formReducer} from 'redux-form/immutable';
-// import {pkgman} from '@truss/core';
-
-// import {createReducer as createResourceReducer} from '../resource'
-
export default function createRootReducer() {
- // dynamicReducers = pkgman.invokeFlat('perseaReducers').reduce(
- // ((reducers, map) -> {reducers..., map...}), {}
- // )
-
return combineReducers({
form: formReducer,
- // resource: createResourceReducer()
-
- // dynamicReducers...
});
}
diff --git a/frontend/root/saga.js b/frontend/root/saga.js
index 8d64e9c..10168d5 100644
--- a/frontend/root/saga.js
+++ b/frontend/root/saga.js
@@ -1,10 +1,8 @@
import {all, call} from 'redux-saga/effects'
-function createRootSaga() {
+export default function createRootSaga() {
return function*() {
return yield all([
].map(call));
};
}
-
-export default createRootSaga;
diff --git a/frontend/root/store.js b/frontend/root/store.js
index 6323a4f..a5bfb6c 100644
--- a/frontend/root/store.js
+++ b/frontend/root/store.js
@@ -1,4 +1,3 @@
-
if ('production' === process.env.NODE_ENV) {
module.exports = require('./store.prod');
}
diff --git a/frontend/root/store.prod.js b/frontend/root/store.prod.js
index 930faa3..3981ee7 100644
--- a/frontend/root/store.prod.js
+++ b/frontend/root/store.prod.js
@@ -3,16 +3,19 @@ import {compose, createStore, applyMiddleware} from 'redux';
import createRootReducer from './reducer';
import createRootSaga from './saga';
+
export default function createRootStore() {
- // sagaMiddleware = createSagaMiddleware()
+ const sagaMiddleware = createSagaMiddleware()
- // store = createStore createRootReducer(), compose(
- // applyMiddleware sagaMiddleware
- // )
- store = createStore(createRootReducer());
+ const store = createStore(
+ createRootReducer(),
+ compose(
+ applyMiddleware(sagaMiddleware)
+ )
+ );
- // sagaMiddleware.run(createRootSaga());
+ sagaMiddleware.run(createRootSaga());
return store
}
diff --git a/package.json b/package.json
index ff20792..46510ac 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"dependencies": {
"@truss/truss": "1.x",
"classnames": "^2.2.6",
+ "contempo": "1.x",
"debug": "3.1.0",
"html-webpack-plugin": "3.2.0",
"immutable": "^3.8.2",
diff --git a/response.dev.js b/response.dev.js
index a85f2b9..9ace1e8 100644
--- a/response.dev.js
+++ b/response.dev.js
@@ -22,14 +22,19 @@ exports.Responder = class Responder {
const address = this.address = httpServer.address();
const config = webpackConfig();
- config.entry.push(
- `webpack-hot-middleware/client?path=http://${
+ for (const i in config.entry) {
+ config.entry[i].unshift(`webpack-hot-middleware/client?path=http://${
frontendHostname}:${address.port
- }/__webpack_hmr`
- );
+ }/__webpack_hmr`);
+ }
const compiler = webpack(config);
this.dm = wdm(compiler, {
+ headers: {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
+ "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
+ },
stats: {
chunkModules: true,
colors: true,
@@ -58,11 +63,15 @@ exports.Responder = class Responder {
httpServer.on('error', console.error);
- httpServer.listen(backendPort);
+ httpServer.on('close', () => httpServer.removeAllListeners());
+ }
+
+ start() {
+ this.server.listen(backendPort);
}
respond({payload: {headers, url}}) {
- headers = Object.assign({}, headers);
+ headers = {...headers};
delete headers.host;
delete headers.connection;
@@ -85,10 +94,17 @@ exports.Responder = class Responder {
data += chunk;
});
res.on('end', () => {
- resolve(data);
+ resolve({
+ html: data,
+ headers: res.headers
+ });
});
});
client.on('error', reject);
});
}
+
+ stop() {
+ this.server.close();
+ }
}
diff --git a/styles/forms.scss b/styles/forms.scss
new file mode 100644
index 0000000..70e10fa
--- /dev/null
+++ b/styles/forms.scss
@@ -0,0 +1,94 @@
+@import './mixins.scss';
+
+button, input, select {
+ background-color: #333;
+ color: #CCC;
+}
+
+button {
+ border: 1px solid #333;
+ padding: 0;
+
+ &:hover {
+ background-color: #555;
+ }
+}
+
+label {
+ border: none;
+ border-bottom: 1px solid #333;
+ margin: 0.25em 0;
+ display: block;
+ font-size: 0.8em;
+ padding: 0.2em;
+}
+
+input {
+ border: 1px solid #333;
+ text-align: right;
+}
+
+.aside {
+ padding: 0.2em;
+}
+
+select {
+ height: 1.5em;
+ line-height: 0;
+ text-align-last: right;
+}
+
+button, select, input {
+ border: 1px solid #333;
+}
+
+form {
+ margin-top: 2em;
+
+ @media (min-width: 1024px) {
+ margin-top: 0;
+ }
+}
+
+.form-row {
+ @include group;
+ margin-bottom: 1em;
+
+ > .control {
+ float: left;
+ }
+}
+
+.control {
+ @include group;
+
+ margin-left: 0.75em;
+ margin-right: 0.75em;
+ text-align: right;
+
+ label {
+ text-align: left;
+ }
+
+ &:first-child {
+ margin-left: 0;
+ }
+ &:last-child {
+ margin-right: 0;
+ }
+}
+
+.truss-number {
+
+ button {
+ background-color: #333;
+
+ &:hover {
+ background-color: #555;
+ }
+
+ span {
+ color: #ccc;
+ }
+ }
+}
diff --git a/styles/global.scss b/styles/global.scss
new file mode 100644
index 0000000..a96633f
--- /dev/null
+++ b/styles/global.scss
@@ -0,0 +1,55 @@
+html, body, .app, .root {
+ width: 100%;
+ height: 100%;
+}
+
+.aside {
+ font-size: 0.8em;
+}
+
+button {
+ &:hover {
+ background-color: #555;
+ }
+}
+
+button, input, select, option {
+
+ &[disabled] {
+ filter: grayscale(1);
+ opacity: 0.5;
+
+ &:hover {
+ background-color: transparent;
+ }
+ }
+
+ &:focus {
+ outline: none;
+ box-shadow: inset 0 0 1pt 0.5pt #CCC, 0 0 1pt 0.5pt #CCC
+ }
+}
+
+hr {
+ border: none;
+ border-bottom: 1px solid #333;
+ margin: 1em 0;
+}
+
+.loading {
+ align-items: center;
+ display: flex;
+ height: 100%;
+ justify-content: center;
+ width: 100%;
+
+ svg {
+ height: 25%;
+ width: 25%;
+ }
+}
+
+.editor.avocado-environment {
+ height: 100%;
+ width: 100%;
+}
diff --git a/styles/mixins.scss b/styles/mixins.scss
new file mode 100644
index 0000000..6e7d0e3
--- /dev/null
+++ b/styles/mixins.scss
@@ -0,0 +1,8 @@
+@mixin group {
+ &:after {
+ content: "";
+ display: table;
+ clear: both;
+ }
+}
+
diff --git a/styles/reset.css b/styles/reset.css
new file mode 100644
index 0000000..a583b75
--- /dev/null
+++ b/styles/reset.css
@@ -0,0 +1,60 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ box-sizing: border-box;
+ color: #CCCCCC;
+ font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+ overflow: hidden;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+ins {
+ text-decoration: none;
+}
+select {
+ font-size: inherit;
+}
+button {
+ line-height: 0;
+}
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 404dc3e..ab2243a 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -17,20 +17,37 @@ module.exports = (config) => {
const SOURCE_PATH = process.env.SOURCE_PATH || '/var/node/src';
const OUTPUT_PATH = process.env.OUTPUT_PATH || '/var/node/dist';
+const hashFormat = {
+ chunk: ('production' === process.env.NODE_ENV) ? '.[chunkhash:20]' : '',
+}
+
module.exports.webpackConfig = function() {
+ const styleDirectory = path.join(SOURCE_PATH, 'styles');
+ const cssPrefix = '!style-loader!css-loader!';
+ const scssPrefix = `${cssPrefix}sass-loader!`;
+
const config = {
mode: 'production' !== process.env.NODE_ENV ? 'development' : 'production',
- entry: [
- '@babel/polyfill',
- path.join(SOURCE_PATH, 'frontend', 'index.js'),
- ],
+ entry: {
+ index: [
+ '@babel/polyfill',
+ path.join(SOURCE_PATH, 'frontend', 'index.js'),
+ ],
+ style: [
+ path.join(cssPrefix, styleDirectory, 'reset.css'),
+ path.join(scssPrefix, styleDirectory, 'global.scss'),
+ path.join(scssPrefix, styleDirectory, 'forms.scss'),
+ ],
+ },
optimization: {},
module: {
rules: [
{
test: /\.js$/,
- exclude: /(node_modules|bower_components)/,
+ exclude: [
+ /(node_modules\/(?!contempo))/,
+ ],
use: {
loader: 'babel-loader',
options: {
@@ -39,6 +56,7 @@ module.exports.webpackConfig = function() {
],
plugins: [
'@babel/plugin-proposal-object-rest-spread',
+ 'react-hot-loader/babel',
],
presets: [
'@babel/preset-react',
@@ -47,10 +65,31 @@ module.exports.webpackConfig = function() {
},
},
},
+ {
+ test: /\.css$/,
+ use: [{
+ loader: 'raw-loader',
+ }],
+ },
+ {
+ test: /\.scss$/,
+ use: [{
+ loader: 'raw-loader',
+ }, {
+ loader: 'sass-loader',
+ options: {
+ sourceMap: 'production' !== process.env.NODE_ENV,
+ },
+ }],
+ },
],
},
+ node: {
+ fs: 'empty',
+ },
output: {
- filename: 'index.js',
+ filename: `[name]${hashFormat.chunk}.js`,
+ chunkFilename: `[id]${hashFormat.chunk}.chunk.js`,
path: path.join(OUTPUT_PATH, 'frontend'),
},
plugins: [
@@ -61,7 +100,10 @@ module.exports.webpackConfig = function() {
],
resolve: {
modules: [path.join(OUTPUT_PATH, 'node_modules')],
- }
+ },
+ resolveLoader: {
+ modules: [path.join(OUTPUT_PATH, 'node_modules')],
+ },
};
if ('production' === config.mode) {
@@ -70,6 +112,7 @@ module.exports.webpackConfig = function() {
];
}
else {
+ config.devtool = 'eval-source-map';
config.plugins.push(new webpack.HotModuleReplacementPlugin());
}