Merge pull request #1 from LordMathis/develop-universal
Develop universal
This commit is contained in:
commit
5b29ef7d6e
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"presets":[
|
||||||
|
"es2015", "react"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"development": {
|
||||||
|
"presets": ["es2015", "react", "stage-0"],
|
||||||
|
"plugins": ["transform-runtime"],
|
||||||
|
"presets": ["react-hmre"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"build": {
|
||||||
|
"presets": ["es2015", "react", "stage-0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,37 +1,7 @@
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules
|
node_modules
|
||||||
jspm_packages
|
dist
|
||||||
|
public
|
||||||
# Optional npm cache directory
|
*.log
|
||||||
.npm
|
content
|
||||||
|
renders
|
||||||
# Optional REPL history
|
data.json
|
||||||
.node_repl_history
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"name": "portfolio",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "portfolio",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "NODE_ENV=production babel src --out-dir dist --copy-files && webpack -p --progress --config webpack.prod.config.js",
|
||||||
|
"start": "NODE_ENV=production node ./src/server.js",
|
||||||
|
"dev": "NODE_ENV=development babel-node ./src/server.js --presets es2015,stage-2 ./srcserver.js"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"porfolio",
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"author": "Matúš Námešný",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"async": "^2.5.0",
|
||||||
|
"axios": "^0.17.0",
|
||||||
|
"express": "^4.13.4",
|
||||||
|
"front-matter": "^2.2.0",
|
||||||
|
"jsonfile": "^4.0.0",
|
||||||
|
"markdown-it": "^8.4.0",
|
||||||
|
"moment": "^2.19.1",
|
||||||
|
"node-sass": "^4.9.0",
|
||||||
|
"react": "^15.0.1",
|
||||||
|
"react-dom": "^15.0.1",
|
||||||
|
"react-router-dom": "^4.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-cli": "^6.26.0",
|
||||||
|
"babel-core": "^6.7.6",
|
||||||
|
"babel-jest": "*",
|
||||||
|
"babel-loader": "^7.1.4",
|
||||||
|
"babel-plugin-transform-runtime": "^6.7.5",
|
||||||
|
"babel-polyfill": "^6.26.0",
|
||||||
|
"babel-preset-es2015": "^6.6.0",
|
||||||
|
"babel-preset-react": "^6.5.0",
|
||||||
|
"babel-preset-react-hmre": "^1.1.1",
|
||||||
|
"babel-preset-stage-0": "^6.5.0",
|
||||||
|
"babel-register": "^6.7.2",
|
||||||
|
"babel-runtime": "^6.26.0",
|
||||||
|
"css-loader": "^0.28.11",
|
||||||
|
"css-modules-require-hook": "^4.0.6",
|
||||||
|
"file-loader": "^1.1.11",
|
||||||
|
"postcss-loader": "^2.1.4",
|
||||||
|
"sass-loader": "^7.0.1",
|
||||||
|
"style-loader": "^0.21.0",
|
||||||
|
"url-loader": "^0.5.9",
|
||||||
|
"webpack": "^4.7.0",
|
||||||
|
"webpack-cli": "^2.1.2",
|
||||||
|
"webpack-dev-middleware": "^3.1.3",
|
||||||
|
"webpack-hot-middleware": "^2.18.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = {};
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {render} from 'react-dom';
|
||||||
|
import {BrowserRouter as Router} from 'react-router-dom';
|
||||||
|
import {App} from './components';
|
||||||
|
|
||||||
|
const AppClient = () => (
|
||||||
|
<Router>
|
||||||
|
<App />
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
|
||||||
|
window.onload = () => {
|
||||||
|
render(
|
||||||
|
<AppClient />,
|
||||||
|
document.getElementById('root')
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Spinner, Header} from '.';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import styles from './About.scss';
|
||||||
|
import contentStyle from '../static/stylesheets/content.scss';
|
||||||
|
|
||||||
|
export default class About extends Component {
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (this.props.isLoading) {
|
||||||
|
return (
|
||||||
|
<div className={contentStyle.contentWrapper} id="about">
|
||||||
|
<Spinner/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={contentStyle.contentWrapper} id="about">
|
||||||
|
<Header header={"About"} />
|
||||||
|
<div className={contentStyle.content} dangerouslySetInnerHTML={{__html: this.props.about.body}}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
@import "../static/stylesheets/variables.scss";
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
import { Home, NotFoundWrapper } from '.';
|
||||||
|
import { MainContainer, PostContainer } from '../containers';
|
||||||
|
|
||||||
|
export const App = () => (
|
||||||
|
<div>
|
||||||
|
<Switch>
|
||||||
|
<Route exact path="/" component={MainContainer} />
|
||||||
|
<Route path="/post/:postname" component={PostContainer} />
|
||||||
|
<Route component={NotFoundWrapper} />
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
|
@ -0,0 +1,42 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Spinner, Header} from '.';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import styles from './Blog.scss';
|
||||||
|
import contentStyle from '../static/stylesheets/content.scss';
|
||||||
|
|
||||||
|
export default class Blog extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.props.isLoading) {
|
||||||
|
return (
|
||||||
|
<div className={contentStyle.contentWrapper} id="blog">
|
||||||
|
<Spinner/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let posts = this.props.posts.map((post) =>
|
||||||
|
<div className={styles.postListItem} key={post.title}>
|
||||||
|
<div className={styles.postHeader}>
|
||||||
|
<div className={styles.post}>
|
||||||
|
<span>{post.published}</span>
|
||||||
|
<a href={post.link}>{post.title}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={contentStyle.contentWrapper} id="blog">
|
||||||
|
<Header header={"Blog"} />
|
||||||
|
|
||||||
|
<div className={contentStyle.content}>
|
||||||
|
<div className={styles.postsWrapper}>
|
||||||
|
{posts}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
@import "../static/stylesheets/variables.scss";
|
||||||
|
|
||||||
|
.post {
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
span {
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: $blue;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: capitalize;
|
||||||
|
font-family: $font-header;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.postsWrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.postHeader {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 5px 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.postListItem {
|
||||||
|
margin: 8px;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import styles from './Header.scss';
|
||||||
|
|
||||||
|
export default class Header extends Component {
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className={styles.mainHeader}>
|
||||||
|
<h1>{this.props.header}</h1>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
@import "../static/stylesheets/variables.scss";
|
||||||
|
|
||||||
|
.mainHeader {
|
||||||
|
border-left: 5px solid $blue;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 0;
|
||||||
|
text-align: left;
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import config from '../utils/config.json';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import styles from './Home.scss';
|
||||||
|
|
||||||
|
export default class Home extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let key = 0;
|
||||||
|
const objKeys = Object.keys(config.social);
|
||||||
|
|
||||||
|
const socialLinks = objKeys.map((val) => {
|
||||||
|
const link = (
|
||||||
|
<a key={key} href={config.social[val]}>
|
||||||
|
<i className={`fa fa-${val} fa-3x`} aria-hidden="true" />
|
||||||
|
<span className="sr-only">{val}</span>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
key += 1;
|
||||||
|
return link;
|
||||||
|
});
|
||||||
|
|
||||||
|
socialLinks.push(
|
||||||
|
<a key={key} href={`mailto:${config.email}`}>
|
||||||
|
<i className="fa fa-envelope-o fa-3x" aria-hidden="true" />
|
||||||
|
<span className="sr-only">e-mail</span>
|
||||||
|
</a>,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id={styles.coverPage} className={styles.coverPageFull}>
|
||||||
|
<div id={styles.coverPageContent}>
|
||||||
|
<div>
|
||||||
|
<h1 id={styles.coverPageName}><Link to="/">{ config.name }</Link></h1>
|
||||||
|
</div>
|
||||||
|
<div className={styles.social}>
|
||||||
|
{socialLinks}
|
||||||
|
</div>
|
||||||
|
<div className={styles.menuLinks}>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="#about">
|
||||||
|
<i className="fa fa-search" aria-hidden="true" /> About
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#blog">
|
||||||
|
<i className="fa fa-pencil-square-o" aria-hidden="true" /> Blog
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
@import "../static/stylesheets/variables.scss";
|
||||||
|
|
||||||
|
#coverPage {
|
||||||
|
background: linear-gradient(140deg,rgba(10, 108, 245, 0.6) 20%, rgba(0, 0, 0, 0.9)), url(../static/images/background-cover-converted.jpg) no-repeat center center fixed;
|
||||||
|
-webkit-background-size: cover;
|
||||||
|
-moz-background-size: cover;
|
||||||
|
background-size: cover;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
color: $white;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coverPage.coverPageFull{
|
||||||
|
height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coverPageName {
|
||||||
|
font-size: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coverPageName a, #coverPageName a:hover {
|
||||||
|
color: $white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social {
|
||||||
|
text-align: center;
|
||||||
|
a {
|
||||||
|
color: $white;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuLinks ul{
|
||||||
|
list-style: none;
|
||||||
|
margin-left: 0;
|
||||||
|
padding: 10px;
|
||||||
|
li {
|
||||||
|
margin: 5px;
|
||||||
|
a {
|
||||||
|
color: $white;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import config from '../utils/config.json';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import styles from './Navbar.scss';
|
||||||
|
|
||||||
|
export default class Navbar extends Component {
|
||||||
|
|
||||||
|
render () {
|
||||||
|
|
||||||
|
let key = 0;
|
||||||
|
const objKeys = Object.keys(config.social);
|
||||||
|
|
||||||
|
const socialLinks = objKeys.map((val) => {
|
||||||
|
const link = (
|
||||||
|
<a key={key} href={config.social[val]}>
|
||||||
|
<i className={`fa fa-${val}`} aria-hidden="true" />
|
||||||
|
<span className="sr-only">{val}</span>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
key += 1;
|
||||||
|
return link;
|
||||||
|
});
|
||||||
|
|
||||||
|
socialLinks.push(
|
||||||
|
<a key={key} href={`mailto:${config.email}`}>
|
||||||
|
<i className="fa fa-envelope-o" aria-hidden="true" />
|
||||||
|
<span className="sr-only">e-mail</span>
|
||||||
|
</a>,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.navbar}>
|
||||||
|
<div className={styles.links}>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href='/'>
|
||||||
|
<span className={styles.nameLink}>{config.name} |</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href='/#about'>
|
||||||
|
<span>About</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href='/#blog'>
|
||||||
|
<span>Blog</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className={styles.social}>
|
||||||
|
{socialLinks}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
@import "../static/stylesheets/variables.scss";
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background: linear-gradient(140deg,rgba(10, 108, 245, 0.6) 20%, rgba(0, 0, 0, 0.9)), url(../static/images/background-cover-converted.jpg) no-repeat center center fixed;
|
||||||
|
color: $white;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
text-align: left;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: $white;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social {
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
vertical-align: bottom;
|
||||||
|
a {
|
||||||
|
color: $white;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.links {
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
li {
|
||||||
|
font-family: $font-header;
|
||||||
|
display: inline;
|
||||||
|
margin: 5px;
|
||||||
|
a {
|
||||||
|
color: $white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nameLink {
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {Navbar, Header} from '.';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import contentStyle from '../static/stylesheets/content.scss';
|
||||||
|
|
||||||
|
export const NotFoundPage = (props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Navbar />
|
||||||
|
<div className={contentStyle.contentWrapper}>
|
||||||
|
<Header header={"Uhm... WHAT?"} />
|
||||||
|
<div className={contentStyle.content}>
|
||||||
|
<p>Looks like you're lost</p>
|
||||||
|
<p>404 Page not found</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotFoundPage;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Wrapper, NotFoundPage} from '.';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import styles from './Wrapper.scss';
|
||||||
|
|
||||||
|
export default class NotFoundWrapper extends Component {
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<NotFoundPage />
|
||||||
|
</Wrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Spinner, Header, Navbar} from '.';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import contentStyle from '../static/stylesheets/content.scss';
|
||||||
|
import styles from './Post.scss';
|
||||||
|
|
||||||
|
export default class Post extends Component {
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if (this.props.isLoading) {
|
||||||
|
return (
|
||||||
|
<div className={contentStyle.contentWrapper}>
|
||||||
|
<Spinner/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Navbar />
|
||||||
|
<div className={contentStyle.contentWrapper}>
|
||||||
|
<Header header={this.props.post.title} />
|
||||||
|
<div className={contentStyle.content}>
|
||||||
|
<div className={styles.postDate}>
|
||||||
|
<h3>{this.props.post.published}</h3>
|
||||||
|
</div>
|
||||||
|
<div className={styles.postContent} dangerouslySetInnerHTML={{__html: this.props.post.body}}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
@import "../static/stylesheets/variables.scss";
|
||||||
|
|
||||||
|
.postContent {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.postDate {
|
||||||
|
float: right;
|
||||||
|
h3 {
|
||||||
|
font-weight: normal
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import styles from './Spinner.scss';
|
||||||
|
|
||||||
|
export default class Spinner extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={styles.spinnerWrapper}>
|
||||||
|
<div className={styles.ldsEllipsis}>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
from https://loading.io/css/
|
||||||
|
*/
|
||||||
|
@import "../static/stylesheets/variables.scss";
|
||||||
|
|
||||||
|
.spinnerWrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ldsEllipsis {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.ldsEllipsis div {
|
||||||
|
position: absolute;
|
||||||
|
top: 27px;
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: $black;
|
||||||
|
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||||
|
}
|
||||||
|
.ldsEllipsis div:nth-child(1) {
|
||||||
|
left: 6px;
|
||||||
|
animation: lds-ellipsis1 0.6s infinite;
|
||||||
|
}
|
||||||
|
.ldsEllipsis div:nth-child(2) {
|
||||||
|
left: 6px;
|
||||||
|
animation: lds-ellipsis2 0.6s infinite;
|
||||||
|
}
|
||||||
|
.ldsEllipsis div:nth-child(3) {
|
||||||
|
left: 26px;
|
||||||
|
animation: lds-ellipsis2 0.6s infinite;
|
||||||
|
}
|
||||||
|
.ldsEllipsis div:nth-child(4) {
|
||||||
|
left: 45px;
|
||||||
|
animation: lds-ellipsis3 0.6s infinite;
|
||||||
|
}
|
||||||
|
@keyframes lds-ellipsis1 {
|
||||||
|
0% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes lds-ellipsis3 {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes lds-ellipsis2 {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(19px, 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Spinner, Header} from '.';
|
||||||
|
import '../static/stylesheets/globals.scss';
|
||||||
|
import styles from './Wrapper.scss';
|
||||||
|
|
||||||
|
export default class Wrapper extends Component {
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className={styles.centerContent}>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
@import "../static/stylesheets/variables.scss";
|
||||||
|
|
||||||
|
.centerContent {
|
||||||
|
text-align: center;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
export { default as Home } from './Home';
|
||||||
|
export { default as Blog } from './Blog';
|
||||||
|
export { default as About } from './About';
|
||||||
|
export { default as Post } from './Post';
|
||||||
|
export { default as NotFoundPage } from './NotFoundPage';
|
||||||
|
export { default as NotFoundWrapper } from './NotFoundWrapper';
|
||||||
|
export { default as Spinner } from './Spinner';
|
||||||
|
export { default as Header } from './Header';
|
||||||
|
export { default as Wrapper } from './Wrapper';
|
||||||
|
export { default as Navbar } from './Navbar';
|
||||||
|
export { default as App } from './App';
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import {About, Blog, Home, Wrapper} from '../components';
|
||||||
|
|
||||||
|
export default class BlogContainer extends Component {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isLoadingBlog: true,
|
||||||
|
isLoadingAbout: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
axios.get('/api/about').then((res) => {
|
||||||
|
this.setState({
|
||||||
|
isLoadingAbout: false,
|
||||||
|
about: res.data,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
axios.get('/api/blog').then((res) => {
|
||||||
|
this.setState({
|
||||||
|
isLoadingBlog: false,
|
||||||
|
posts: res.data,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Home/>
|
||||||
|
<Wrapper>
|
||||||
|
<About isLoading={this.state.isLoadingAbout}
|
||||||
|
about={this.state.about}/>
|
||||||
|
<Blog isLoading={this.state.isLoadingBlog}
|
||||||
|
posts={this.state.posts}/>
|
||||||
|
</Wrapper>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import {Post, Wrapper, NotFoundPage} from '../components';
|
||||||
|
|
||||||
|
export default class PostContainer extends Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isLoading: true,
|
||||||
|
error: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const url = '/api/post/' + this.props.match.params.postname;
|
||||||
|
|
||||||
|
axios.get(url).then((res) => {
|
||||||
|
if (res.data.error) {
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setState({
|
||||||
|
error: false,
|
||||||
|
isLoading: false,
|
||||||
|
post: res.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if (this.state.error) {
|
||||||
|
return (
|
||||||
|
<NotFoundPage />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<Post isLoading={this.state.isLoading}
|
||||||
|
post={this.state.post} />
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as MainContainer } from './MainContainer';
|
||||||
|
export { default as PostContainer } from './PostContainer';
|
|
@ -0,0 +1,56 @@
|
||||||
|
require('babel-register');
|
||||||
|
|
||||||
|
var app = new (require('express'))();
|
||||||
|
var port = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
const sass = require('node-sass');
|
||||||
|
|
||||||
|
require('css-modules-require-hook')({
|
||||||
|
generateScopedName: '[name]__[local]___[hash:base64:5]',
|
||||||
|
extensions: ['.scss', '.css'],
|
||||||
|
preprocessCss: (data, filename) => sass.renderSync({
|
||||||
|
data,
|
||||||
|
file: filename,
|
||||||
|
}).css
|
||||||
|
});
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var filename = './src/utils/data.json';
|
||||||
|
var dataStub = {"posts": [], "other": []};
|
||||||
|
fs.writeFileSync(filename, JSON.stringify(dataStub));
|
||||||
|
|
||||||
|
|
||||||
|
// initalize webpack dev middleware if in development context
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
var webpack = require('webpack')
|
||||||
|
var config = require('../webpack.config')
|
||||||
|
|
||||||
|
var devMiddleware = require('webpack-dev-middleware')
|
||||||
|
var hotDevMiddleware = require('webpack-hot-middleware')
|
||||||
|
var compiler = webpack(config)
|
||||||
|
var devMiddlewareConfig = {
|
||||||
|
noInfo: true,
|
||||||
|
stats: {colors: true},
|
||||||
|
publicPath: config.output.publicPath
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(devMiddleware(compiler, devMiddlewareConfig))
|
||||||
|
app.use(hotDevMiddleware(compiler))
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(require('express').static('public'));
|
||||||
|
require('./utils/scanner')();
|
||||||
|
|
||||||
|
var api = require('./utils/api');
|
||||||
|
app.use("/api", api);
|
||||||
|
|
||||||
|
var serverRender = require('./utils/serverRender');
|
||||||
|
app.get("*", serverRender);
|
||||||
|
|
||||||
|
app.listen(port, function(error) {
|
||||||
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
} else {
|
||||||
|
console.info("[Server] Listening on port %s", port);
|
||||||
|
}
|
||||||
|
})
|
Binary file not shown.
After Width: | Height: | Size: 289 KiB |
|
@ -0,0 +1,20 @@
|
||||||
|
@import "./variables.scss";
|
||||||
|
|
||||||
|
.contentWrapper {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20px;
|
||||||
|
a {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
width: 100%;
|
||||||
|
@media (min-width: $break-large) {
|
||||||
|
width: 960px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: left;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
@import "./variables.scss";
|
||||||
|
|
||||||
|
:global(body, html) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(body) {
|
||||||
|
font-family: $font-paragraph;
|
||||||
|
color: $black;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 1 through 6 {
|
||||||
|
:global(h#{$i}) {
|
||||||
|
font-family: $font-header;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
$font-header: 'Open Sans Condensed', sans-serif;
|
||||||
|
$font-paragraph: 'Open Sans', sans-serif;
|
||||||
|
$white: #fdfdfd;
|
||||||
|
$black: #2f2f2f;
|
||||||
|
$blue: #144A98;
|
||||||
|
$grey: #A9A9A9;
|
||||||
|
$break-large: 992px;
|
|
@ -0,0 +1,50 @@
|
||||||
|
const data = require('./data.json');
|
||||||
|
const api = require('express').Router();
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const config = require('./config.json');
|
||||||
|
|
||||||
|
api.get('/blog', (req, res) => {
|
||||||
|
res.json(data.posts);
|
||||||
|
});
|
||||||
|
|
||||||
|
api.get('/about', (req, res) => {
|
||||||
|
const renderPath = path.join(process.cwd(), '/renders', 'about.html');
|
||||||
|
fs.readFile(renderPath, 'utf8', (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
res.json({
|
||||||
|
error: 404
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.json({
|
||||||
|
body: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
api.get('/post/:postname', (req, res) => {
|
||||||
|
const postname = req.params.postname;
|
||||||
|
const post = data.posts.find((el) => {
|
||||||
|
return el.filename === postname
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderPath = path.join(process.cwd(), '/renders', postname + '.html');
|
||||||
|
fs.readFile(renderPath, 'utf8', (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
res.json({
|
||||||
|
error: 404
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.json({
|
||||||
|
published: post.published,
|
||||||
|
link: post.link,
|
||||||
|
title: post.title,
|
||||||
|
body: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = api;
|
|
@ -0,0 +1,128 @@
|
||||||
|
const MarkdownIt = require('markdown-it');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const moment = require('moment');
|
||||||
|
const jsonfile = require('jsonfile');
|
||||||
|
const async = require('async');
|
||||||
|
const fm = require('front-matter');
|
||||||
|
const config = require('../utils/config.json');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders file using MarkdownIt
|
||||||
|
*/
|
||||||
|
function render(file) {
|
||||||
|
const md = new MarkdownIt({html: true});
|
||||||
|
return md.render(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts file metadata such as parent directory
|
||||||
|
*/
|
||||||
|
function fileMetadata(filepath) {
|
||||||
|
const paths = filepath.split('/');
|
||||||
|
const basename = path.basename(filepath);
|
||||||
|
|
||||||
|
const metadata = {
|
||||||
|
basename,
|
||||||
|
filename: basename.substr(0, basename.lastIndexOf('.')),
|
||||||
|
parrent: paths[paths.length - 2],
|
||||||
|
dirname: path.dirname(filepath),
|
||||||
|
};
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles file that is a blog post
|
||||||
|
*/
|
||||||
|
function compilePost(filepath, data, fileData, callback) {
|
||||||
|
const frontMatter = fm(fileData);
|
||||||
|
const rendered = render(frontMatter.body);
|
||||||
|
const metadata = fileMetadata(filepath);
|
||||||
|
|
||||||
|
let published;
|
||||||
|
if (frontMatter.attributes.date) {
|
||||||
|
published = moment(frontMatter.attributes.date);
|
||||||
|
} else {
|
||||||
|
published = moment();
|
||||||
|
}
|
||||||
|
|
||||||
|
const post = {
|
||||||
|
published: published.format('MMMM DD, YYYY'),
|
||||||
|
filename: metadata.filename,
|
||||||
|
title: frontMatter.attributes.title,
|
||||||
|
link: '/post/' + metadata.filename
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderedpath = path.join(process.cwd(), config.renderPath, `${metadata.filename}.html`);
|
||||||
|
|
||||||
|
fs.writeFile(renderedpath, rendered, (err) => {
|
||||||
|
if (err) callback(err);
|
||||||
|
else callback(null, post);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles other types of files such as resumes, about me and so on.
|
||||||
|
*/
|
||||||
|
function compileOther(filepath, data, fileData, callback) {
|
||||||
|
|
||||||
|
const frontMatter = fm(fileData);
|
||||||
|
const rendered = render(frontMatter.body);
|
||||||
|
const metadata = fileMetadata(filepath);
|
||||||
|
|
||||||
|
const post = {
|
||||||
|
filename: metadata.filename
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderedpath = path.join(process.cwd(), config.renderPath, `${metadata.filename}.html`);
|
||||||
|
|
||||||
|
fs.writeFile(renderedpath, rendered, (err) => {
|
||||||
|
if (err) callback(err);
|
||||||
|
else callback(null, post);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function Compiler(data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Compiler.prototype.addFile = function(filepath, isPost, callback) {
|
||||||
|
|
||||||
|
if (isPost) {
|
||||||
|
async.waterfall([
|
||||||
|
fs.readFile.bind(fs, filepath, 'utf8'),
|
||||||
|
compilePost.bind(compilePost, filepath, this.data),
|
||||||
|
], (err, result) => {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
this.data.posts.push(result);
|
||||||
|
console.log("[Compiler] File %s compiled", filepath);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
async.waterfall([
|
||||||
|
fs.readFile.bind(fs, filepath, 'utf8'),
|
||||||
|
compileOther.bind(compileOther, filepath, this.data),
|
||||||
|
], (err, result) => {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
this.data.other.push(result);
|
||||||
|
console.log("[Compiler] File %s compiled", filepath);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes updated data to the data file
|
||||||
|
*/
|
||||||
|
Compiler.prototype.writeData = function(callback) {
|
||||||
|
const dataPath = path.join(process.cwd(), 'src/utils/data.json');
|
||||||
|
jsonfile.writeFile(dataPath, this.data, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Compiler;
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"title": "Matúš Námešný",
|
||||||
|
"coverimage": "",
|
||||||
|
"name": "Matúš Námešný",
|
||||||
|
"email": "matus@namesny.com",
|
||||||
|
"social": {
|
||||||
|
"twitter": "https://twitter.com/matus_n",
|
||||||
|
"github": "https://github.com/LordMathis",
|
||||||
|
"codepen": "https://codepen.io/LordMathis/",
|
||||||
|
"linkedin": "https://www.linkedin.com/in/mat%C3%BA%C5%A1-n%C3%A1me%C5%A1n%C3%BD-3903b6128/"
|
||||||
|
},
|
||||||
|
"contentPath": "./content",
|
||||||
|
"renderPath": "./renders",
|
||||||
|
"files": [
|
||||||
|
"about.md", "resume.md"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const async = require('async');
|
||||||
|
const Compiler = require('./compiler');
|
||||||
|
const config = require('./config.json');
|
||||||
|
const data = require('./data.json');
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
|
||||||
|
var compiler = new Compiler(data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the directory and returns it's content
|
||||||
|
*/
|
||||||
|
function readdir(callback) {
|
||||||
|
fs.readdir(config.contentPath, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls compile on each file in the directory
|
||||||
|
*/
|
||||||
|
function compile(files, callback) {
|
||||||
|
console.log("[Scanner] Discovered files: " + files);
|
||||||
|
async.each(files, compileFile, (err) => {
|
||||||
|
if (err) throw err;
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function which calls compile in the Compiler module
|
||||||
|
*/
|
||||||
|
function compileFile(file, callback) {
|
||||||
|
const filePath = path.join(process.cwd(), config.contentPath, file);
|
||||||
|
|
||||||
|
// config.files contains list of file names which are not considered blog posts
|
||||||
|
if (config.files.indexOf(file) == -1) {
|
||||||
|
compiler.addFile(filePath, true, callback);
|
||||||
|
} else {
|
||||||
|
compiler.addFile(filePath, false, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes updated data into the data file
|
||||||
|
*/
|
||||||
|
function writeData(callback) {
|
||||||
|
compiler.writeData(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main function. Scans the directory for files and compiles them into html
|
||||||
|
* using the Compiler module
|
||||||
|
*/
|
||||||
|
async.waterfall([
|
||||||
|
readdir,
|
||||||
|
compile,
|
||||||
|
writeData
|
||||||
|
], (err) => {
|
||||||
|
if(err) throw err;
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
//import 'babel-polyfill'
|
||||||
|
import React from 'react'
|
||||||
|
import { renderToString } from 'react-dom/server'
|
||||||
|
import { StaticRouter as Router } from 'react-router-dom'
|
||||||
|
import { App } from '../components/App'
|
||||||
|
|
||||||
|
function serverRender(req, res) {
|
||||||
|
let markup = '';
|
||||||
|
let status = 200;
|
||||||
|
|
||||||
|
const context = {}
|
||||||
|
markup = renderToString(
|
||||||
|
<Router location={req.url} context={context}>
|
||||||
|
<App />
|
||||||
|
</Router>,
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.status(status).send(renderFullPage(markup));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFullPage(html) {
|
||||||
|
return `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Matúš Námešný</title>
|
||||||
|
<!-- Google Fonts -->
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans|Open+Sans+Condensed:700&subset=latin-ext" rel="stylesheet">
|
||||||
|
<!-- Font Awesome -->
|
||||||
|
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root">${process.env.NODE_ENV === 'production' ? html : `<div>${html}</div>`}</div>
|
||||||
|
<script src="/static/bundle.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = serverRender;
|
|
@ -0,0 +1,72 @@
|
||||||
|
const { resolve, join } = require('path')
|
||||||
|
const webpack = require('webpack')
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
mode: 'development',
|
||||||
|
devtool: 'cheap-eval-source-map',
|
||||||
|
context: resolve(__dirname, 'src'),
|
||||||
|
entry: {
|
||||||
|
home: [
|
||||||
|
'webpack-hot-middleware/client',
|
||||||
|
'./app-client.js'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: resolve(__dirname,'public/static'),
|
||||||
|
filename: 'bundle.js',
|
||||||
|
publicPath: '/static/'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
use: [
|
||||||
|
'babel-loader'
|
||||||
|
],
|
||||||
|
exclude: '/node_modules/'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: "style-loader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
modules: true,
|
||||||
|
importLoaders: 2,
|
||||||
|
localIdentName: '[name]__[local]___[hash:base64:5]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "postcss-loader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "sass-loader"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpg)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'url-loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(html)$/,
|
||||||
|
use: {
|
||||||
|
loader: 'html-loader',
|
||||||
|
options: {
|
||||||
|
attrs: [':data-src']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
new webpack.NoEmitOnErrorsPlugin(),
|
||||||
|
new webpack.NamedModulesPlugin()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
module.exports = config
|
|
@ -0,0 +1,57 @@
|
||||||
|
const { resolve, join } = require('path')
|
||||||
|
const webpack = require('webpack')
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
mode: 'production',
|
||||||
|
context: resolve(__dirname, 'src'),
|
||||||
|
entry: {
|
||||||
|
bundle: [
|
||||||
|
'./app-client.js'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: resolve(__dirname, 'public/static'),
|
||||||
|
filename: '[name].js',
|
||||||
|
publicPath: '/static/'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
use: [
|
||||||
|
'babel-loader'
|
||||||
|
],
|
||||||
|
exclude: '/node_modules/'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
modules: true,
|
||||||
|
importLoaders: 2,
|
||||||
|
localIdentName: '[name]__[local]___[hash:base64:5]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "postcss-loader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'sass-loader'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpg)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'url-loader'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = config
|
Loading…
Reference in New Issue