diff --git a/.eslintrc b/.eslintrc index 40b7e07..8cf65df 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,24 +1,22 @@ { - "plugins": [ - "react" - ], - "parserOptions": { - "sourceType": "module", - "ecmaFeatures": { - "jsx": true, - "modules": true - } - }, - "env": { - "browser": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:react/recommended" - ], - "rules": { - "quotes": [ "error", "single" ], - "semi": [ "error", "never" ] + "plugins": ["react", "graphql"], + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "jsx": true, + "modules": true } + }, + "env": { + "browser": true, + "node": true + }, + "globals": { + "graphql": true + }, + "extends": ["eslint:recommended", "plugin:react/recommended"], + "rules": { + "quotes": ["error", "single"], + "semi": ["error", "never"] + } } diff --git a/.gitignore b/.gitignore index c93382f..5cebd59 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,12 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. +# Project dependencies +.cache +node_modules +yarn-error.log -# dependencies -/node_modules - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local +# Build directory +/public npm-debug.log* yarn-debug.log* -yarn-error.log* yarn.lock package-lock.json -src/**/*.css diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6b02c0c..8184d4f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ cache: testing: stage: test script: - - npm install + - npm i gatsby-cli -g + - npm i - npm test - - npm run build-css - - npm run build-js + - npm run build diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..36301bc --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/src/data/meta.json b/data/meta.json similarity index 86% rename from src/data/meta.json rename to data/meta.json index f9c1273..988d9a9 100644 --- a/src/data/meta.json +++ b/data/meta.json @@ -2,6 +2,7 @@ "title": "Matthias Kretschmann", "tagline": "Designer & Developer", "description": "", + "url": "https://matthiaskretschmann.com", "social": { "Twitter": "https://twitter.com/kremalicious", "GitHub": "https://github.com/kremalicious", diff --git a/src/data/projects.json b/data/projects.json similarity index 100% rename from src/data/projects.json rename to data/projects.json diff --git a/gatsby-browser.js b/gatsby-browser.js new file mode 100644 index 0000000..d168926 --- /dev/null +++ b/gatsby-browser.js @@ -0,0 +1,7 @@ +/** + * Implement Gatsby's Browser APIs in this file. + * + * See: https://www.gatsbyjs.org/docs/browser-apis/ + */ + + // You can delete this file if you're not using it \ No newline at end of file diff --git a/gatsby-config.js b/gatsby-config.js new file mode 100644 index 0000000..9e679a4 --- /dev/null +++ b/gatsby-config.js @@ -0,0 +1,29 @@ +const meta = require('./data/meta.json') + +module.exports = { + siteMetadata: { + siteUrl: `${meta.url}`, + }, + plugins: [ + 'gatsby-plugin-react-next', + 'gatsby-plugin-react-helmet', + 'gatsby-transformer-json', + 'gatsby-plugin-sitemap', + { + resolve: 'gatsby-source-filesystem', + options: { + name: 'data', + path: `${__dirname}/data/`, + }, + }, + { + resolve: 'gatsby-plugin-sass', + options: { + includePaths: [ + `${__dirname}/node_modules`, + `${__dirname}/src/styles` + ], + }, + } + ] +} diff --git a/gatsby-node.js b/gatsby-node.js new file mode 100644 index 0000000..99f1e2e --- /dev/null +++ b/gatsby-node.js @@ -0,0 +1,52 @@ +const path = require('path') + +// Implement the Gatsby API “createPages”. This is called once the +// data layer is bootstrapped to let plugins create pages from data. +exports.createPages = ({ boundActionCreators, graphql }) => { + const { createPage } = boundActionCreators + + return new Promise((resolve, reject) => { + const template = path.resolve('src/layouts/Project.js') + + resolve( + graphql(` + { + allProjectsJson { + edges { + node { + title + slug + img + img_more + links { + Link + GitHub + Info + } + description + techstack + } + } + } + } + `).then(result => { + if (result.errors) { + reject(result.errors) + } + + console.log(result) + + result.data.allProjectsJson.edges.forEach(({ node }) => { + const slug = node.slug + + createPage({ + path: slug, + component: template, + context: { slug: slug }, + }) + }) + + resolve() + })) + }) +} diff --git a/gatsby-ssr.js b/gatsby-ssr.js new file mode 100644 index 0000000..cdad094 --- /dev/null +++ b/gatsby-ssr.js @@ -0,0 +1,7 @@ +/** + * Implement Gatsby's SSR (Server Side Rendering) APIs in this file. + * + * See: https://www.gatsbyjs.org/docs/ssr-apis/ + */ + + // You can delete this file if you're not using it \ No newline at end of file diff --git a/package.json b/package.json index 03088e8..0b438ff 100644 --- a/package.json +++ b/package.json @@ -3,38 +3,34 @@ "version": "0.1.0", "private": true, "dependencies": { - "node-sass-chokidar": "^1.2.2", - "npm-run-all": "^4.1.2", - "react": "^16.3.0", - "react-dom": "^16.3.0", + "gatsby": "^1.9.241", + "gatsby-link": "^1.6.39", + "gatsby-plugin-react-helmet": "^2.0.8", + "gatsby-plugin-react-next": "^1.0.11", + "gatsby-plugin-sass": "latest", + "gatsby-plugin-sitemap": "^1.2.20", + "gatsby-source-filesystem": "^1.5.28", + "gatsby-transformer-json": "^1.0.16", "react-helmet": "^5.2.0", - "react-lazyload": "^2.3.0", "react-markdown": "^3.3.0", - "react-router-dom": "^4.2.2", - "react-scripts": "1.1.1", "react-transition-group": "^2.3.0" }, "devDependencies": { "babel-eslint": "^8.2.2", "eslint": "^4.19.1", + "eslint-plugin-graphql": "^1.5.0", "eslint-plugin-react": "^7.7.0", - "jest-cli": "^22.4.3", - "react-snap": "^1.12.0", + "prettier": "^1.11.1", "stylelint": "^9.2.0", "stylelint-config-standard": "^18.2.0" }, "scripts": { - "lint:js": "eslint ./{src,public}/**/*.js", + "lint:js": "eslint ./src/**/*.js", "lint:css": "stylelint ./src/**/*.scss", "lint": "npm run lint:js && npm run lint:css", - "build-css": "node-sass-chokidar --include-path src/stylesheets/ src/ -o src/", - "watch-css": "npm run build-css && node-sass-chokidar --include-path src/stylesheets/ src/ -o src/ --watch --recursive", - "start-js": "react-scripts start", - "start": "npm-run-all -p watch-css start-js", - "build-js": "react-scripts build", - "build": "npm-run-all build-css build-js", - "test": "npm run lint && react-scripts test --env=jsdom", - "eject": "react-scripts eject", - "postbuild": "react-snap" + "build": "gatsby build", + "start": "gatsby develop", + "format": "prettier --write 'src/**/*.js'", + "test": "npm run lint" } } diff --git a/public/index.html b/public/index.html deleted file mode 100644 index a36baf9..0000000 --- a/public/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - matthias kretschmann { designer & developer } - - - -
- - diff --git a/public/manifest.json b/public/manifest.json deleted file mode 100644 index f31dacd..0000000 --- a/public/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "short_name": "Matthias Kretschmann", - "name": "Portfolio of Designer & Developer Matthias Kretschmann", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - } - ], - "start_url": "./index.html", - "display": "standalone", - "theme_color": "#015565", - "background_color": "#e7eef4" -} diff --git a/src/App.js b/src/App.js deleted file mode 100644 index 83606f8..0000000 --- a/src/App.js +++ /dev/null @@ -1,27 +0,0 @@ -import React, { Fragment } from 'react' -import Helmet from 'react-helmet/es/Helmet' -import Routes from './Routes' -import FadeIn from './components/atoms/FadeIn' -import Footer from './components/molecules/Footer' -import meta from './data/meta.json' - -const Head = () => ( - -) - -const App = () => ( - - - -
- -
-
-
-
-) - -export default App diff --git a/src/App.test.js b/src/App.test.js deleted file mode 100644 index 487ede5..0000000 --- a/src/App.test.js +++ /dev/null @@ -1,11 +0,0 @@ -/* global it */ - -import React from 'react' -import ReactDOM from 'react-dom' -import App from './App' - -it('renders without crashing', () => { - const div = document.createElement('div') - ReactDOM.render(, div) - ReactDOM.unmountComponentAtNode(div) -}) diff --git a/src/Routes.js b/src/Routes.js deleted file mode 100644 index 26bdcae..0000000 --- a/src/Routes.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react' -import Switch from 'react-router-dom/Switch' -import Route from 'react-router-dom/Route' -import Home from './components/pages/Home' -import Project from './components/templates/Project' -import NotFound from './components/pages/NotFound' -import projects from './data/projects.json' - -const Routes = () => ( - - - {projects.map(project => ( - - - } - /> - ))} - - -) - -export default Routes diff --git a/src/components/atoms/Content.js b/src/components/atoms/Content.js index 06a9cbc..95a5b24 100644 --- a/src/components/atoms/Content.js +++ b/src/components/atoms/Content.js @@ -1,6 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' -import './Content.css' +import './Content.scss' const Content = ({children}) => (
diff --git a/src/components/atoms/FadeIn.js b/src/components/atoms/FadeIn.js index b019bcf..bfdf5dc 100644 --- a/src/components/atoms/FadeIn.js +++ b/src/components/atoms/FadeIn.js @@ -1,6 +1,6 @@ import React from 'react' import CSSTransition from 'react-transition-group/CSSTransition' -import './FadeIn.css' +import './FadeIn.scss' const FadeIn = (props) => ( (
diff --git a/src/components/atoms/Icons.js b/src/components/atoms/Icons.js index cda32a2..1fd0ca3 100644 --- a/src/components/atoms/Icons.js +++ b/src/components/atoms/Icons.js @@ -1,5 +1,5 @@ import React from 'react' -import './Icons.css' +import './Icons.scss' export const Facebook = props => ( diff --git a/src/components/molecules/Footer.js b/src/components/molecules/Footer.js index 485bfd5..776ef71 100644 --- a/src/components/molecules/Footer.js +++ b/src/components/molecules/Footer.js @@ -1,6 +1,6 @@ import React from 'react' -import meta from '../../data/meta.json' -import './Footer.css' +import meta from '../../../data/meta.json' +import './Footer.scss' const Footer = () => { const year = new Date().getFullYear() diff --git a/src/components/molecules/Header.js b/src/components/molecules/Header.js index 0b2ce8f..0a6c8c4 100644 --- a/src/components/molecules/Header.js +++ b/src/components/molecules/Header.js @@ -2,8 +2,8 @@ import React, { Component } from 'react' import Link from 'react-router-dom/Link' import PropTypes from 'prop-types' import Social from './Social' -import './Header.css' -import meta from '../../data/meta.json' +import './Header.scss' +import meta from '../../../data/meta.json' class Header extends Component { render() { diff --git a/src/components/molecules/Social.js b/src/components/molecules/Social.js index 1bd86c9..b00a6f3 100644 --- a/src/components/molecules/Social.js +++ b/src/components/molecules/Social.js @@ -1,7 +1,7 @@ import React from 'react' import { Twitter, GitHub, Facebook } from '../atoms/Icons' -import meta from '../../data/meta.json' -import './Social.css' +import meta from '../../../data/meta.json' +import './Social.scss' const social = meta.social diff --git a/src/components/organisms/Projects.js b/src/components/organisms/Projects.js index a5aaa6c..1fbbb8e 100644 --- a/src/components/organisms/Projects.js +++ b/src/components/organisms/Projects.js @@ -1,29 +1,26 @@ import React from 'react' -import Link from 'react-router-dom/Link' -import LazyLoad from 'react-lazyload' +import PropTypes from 'prop-types' +import Link from 'gatsby-link' import FadeIn from '../atoms/FadeIn' -import projects from '../../data/projects.json' import images from '../../images' -import './Projects.css' +import './Projects.scss' -const Projects = () => ( -
- {projects.map(project => ( - - - -

{project.title}

+const Projects = ({ data }) => { + const projects = data.allProjectsJson - {project.title} + return
+ {projects.edges.map(({ node }) => + +

{node.title}

+ + {node.title} -
- - ))} -
-) +
)} +
+} + +Projects.propTypes = { + data: PropTypes.object, +} export default Projects diff --git a/src/components/pages/Home.js b/src/components/pages/Home.js deleted file mode 100644 index a015dfd..0000000 --- a/src/components/pages/Home.js +++ /dev/null @@ -1,18 +0,0 @@ -import React, { Component, Fragment } from 'react' -import Header from '../molecules/Header' -import Projects from '../organisms/Projects' - -class Home extends Component { - render() { - return ( - -
-
- -
- - ) - } -} - -export default Home diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 86b6100..0000000 --- a/src/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' -import { hydrate, render } from 'react-dom' -import Router from 'react-router-dom/BrowserRouter' -import './index.css' -import App from './App' -import registerServiceWorker from './registerServiceWorker' - -const rootElement = document.getElementById('root') - -if (rootElement.hasChildNodes()) { - hydrate(, rootElement) -} else { - render(, rootElement) -} - -registerServiceWorker() diff --git a/src/components/templates/Project.js b/src/layouts/Project.js similarity index 72% rename from src/components/templates/Project.js rename to src/layouts/Project.js index 7ab6f69..bb6ccc1 100644 --- a/src/components/templates/Project.js +++ b/src/layouts/Project.js @@ -1,14 +1,15 @@ -import React, { Fragment } from 'react' +import React from 'react' import PropTypes from 'prop-types' -import Helmet from 'react-helmet/es/Helmet' +import Helmet from 'react-helmet' import ReactMarkdown from 'react-markdown' -import Header from '../molecules/Header' -import Content from '../atoms/Content' -import FullWidth from '../atoms/FullWidth' -import images from '../../images' -import './Project.css' +import Header from '../components/molecules/Header' +import Content from '../components/atoms/Content' +import FullWidth from '../components/atoms/FullWidth' +import images from '../images' +import './Project.scss' -const Project = ({ project }) => { +const Project = ({ data }) => { + const project = data.allProjectsJson.edges[0].node const title = project.title const img = project.img const img_more = project.img_more @@ -17,7 +18,7 @@ const Project = ({ project }) => { const techstack = project.techstack return ( - +
{title} @@ -67,12 +68,34 @@ const Project = ({ project }) => { - +
) } Project.propTypes = { - project: PropTypes.object + data: PropTypes.object } export default Project + +export const query = graphql` + query ProjectQuery($slug: String) { + allProjectsJson(filter: { slug: { eq: $slug } }) { + edges { + node { + title + slug + img + img_more + links { + Link + GitHub + Info + } + description + techstack + } + } + } + } +` diff --git a/src/components/templates/Project.scss b/src/layouts/Project.scss similarity index 100% rename from src/components/templates/Project.scss rename to src/layouts/Project.scss diff --git a/src/layouts/index.js b/src/layouts/index.js new file mode 100644 index 0000000..608bc66 --- /dev/null +++ b/src/layouts/index.js @@ -0,0 +1,30 @@ +import React from 'react' +import PropTypes from 'prop-types' +import Helmet from 'react-helmet' +import FadeIn from '../components/atoms/FadeIn' +import Footer from '../components/molecules/Footer' +import meta from '../../data/meta.json' +import './index.scss' + +const Head = () => ( + + + +) + +const TemplateWrapper = ({ children }) => ( +
+ + {children()} +
+
+) + +TemplateWrapper.propTypes = { + children: PropTypes.func, +} + +export default TemplateWrapper diff --git a/src/index.scss b/src/layouts/index.scss similarity index 100% rename from src/index.scss rename to src/layouts/index.scss diff --git a/src/components/pages/NotFound.js b/src/pages/404.js similarity index 100% rename from src/components/pages/NotFound.js rename to src/pages/404.js diff --git a/src/pages/index.js b/src/pages/index.js new file mode 100644 index 0000000..8967e18 --- /dev/null +++ b/src/pages/index.js @@ -0,0 +1,34 @@ +import React from 'react' +import PropTypes from 'prop-types' +import Header from '../components/molecules/Header' +import Projects from '../components/organisms/Projects' + +const Home = ({ data }) => ( +
+
+
+ +
+
+) + +Home.propTypes = { + data: PropTypes.object, +} + +export default Home + +export const query = graphql` + query IndexQuery { + allProjectsJson { + totalCount + edges { + node { + title + slug + img + } + } + } + } +` diff --git a/src/registerServiceWorker.js b/src/registerServiceWorker.js deleted file mode 100644 index 7dea265..0000000 --- a/src/registerServiceWorker.js +++ /dev/null @@ -1,112 +0,0 @@ -// In production, we register a service worker to serve assets from local cache. - -// This lets the app load faster on subsequent visits in production, and gives -// it offline capabilities. However, it also means that developers (and users) -// will only see deployed updates on the "N+1" visit to a page, since previously -// cached resources are updated in the background. - -// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. -// This link also includes instructions on opting out of this behavior. - -const isLocalhost = Boolean( - window.location.hostname === 'localhost' || - // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) -) - -export default function register() { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location) - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 - return - } - - window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` - - if (isLocalhost) { - // This is running on localhost. Lets check if a service worker still exists or not. - checkValidServiceWorker(swUrl) - - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log('This web app is being served cache-first by a service worker. To learn more, visit https://goo.gl/SC7cgQ') // eslint-disable-line no-console - }) - } else { - // Is not local host. Just register service worker - registerValidSW(swUrl) - } - }) - } -} - -function registerValidSW(swUrl) { - navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the old content will have been purged and - // the fresh content will have been added to the cache. - // It's the perfect time to display a "New content is - // available; please refresh." message in your web app. - console.log('New content is available; please refresh.') // eslint-disable-line no-console - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.') // eslint-disable-line no-console - } - } - } - } - }) - .catch(error => { - console.error('Error during service worker registration:', error) // eslint-disable-line no-console - }) -} - -function checkValidServiceWorker(swUrl) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then(response => { - // Ensure service worker exists, and that we really are getting a JS file. - if ( - response.status === 404 || - response.headers.get('content-type').indexOf('javascript') === -1 - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload() - }) - }) - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl) - } - }) - .catch(() => { - console.log('No internet connection found. App is running in offline mode.') // eslint-disable-line no-console - }) -} - -export function unregister() { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.ready.then(registration => { - registration.unregister() - }) - } -} diff --git a/src/stylesheets/_variables.scss b/src/styles/_variables.scss similarity index 100% rename from src/stylesheets/_variables.scss rename to src/styles/_variables.scss