Start migration to NextJS

This commit is contained in:
Aarni Halinen
2020-12-02 01:57:59 +02:00
parent b444c8e2f6
commit 7edf8b24cc
28 changed files with 2778 additions and 10461 deletions
+7 -24
View File
@@ -1,33 +1,16 @@
{
"presets": [
["env", {"modules": false}],
"react"
"next/babel"
],
"plugins": [
"react-hot-loader/babel",
[
"babel-plugin-styled-components",
{ "ssr": true, "displayName": true, "preprocess": false }
]
],
"env": {
"production": {
"plugins": [
[
"babel-plugin-styled-components",
{ "ssr": true, "displayName": true, "preprocess": false }
{
"ssr": true,
"displayName": true,
"preprocess": false,
"pure": true
}
]
],
"presets": ["minify"]
},
"test": {
"plugins": [
[
"babel-plugin-styled-components",
{ "ssr": true, "displayName": true, "preprocess": false }
]
],
"presets": ["env", "react"]
}
}
}
+33 -6
View File
@@ -1,10 +1,37 @@
.idea/
dist/
node_modules/
src/**/*.jsx
.vscode/
tests/jest/__coverage__/
tests/jest/**/*.jsx
tests/testcafe/screenshots
.vscode/
.env
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel
+30
View File
@@ -0,0 +1,30 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
-58
View File
@@ -1,58 +0,0 @@
// Shared config (dev and prod)
const {resolve} = require("path");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
const StyleLintPlugin = require("stylelint-webpack-plugin");
const webpack = require('webpack');
const Dotenv = require("dotenv-webpack");
module.exports = function (env, argv) {
const config = {};
config.output = {
publicPath: "/",
};
config.resolve = {
extensions: [".ts", ".tsx", ".js", ".jsx"],
plugins: [new TsconfigPathsPlugin()]
};
config.context = resolve(__dirname, "../../src");
config.module = {
rules: [],
};
config.module.rules.push({
test: /\.js$/,
use: ["babel-loader", "source-map-loader"],
exclude: /node_modules/
});
config.module.rules.push({
test: /\.tsx?$/,
use: ["babel-loader", "ts-loader"]
});
config.module.rules.push({
test: /\.(jpe?g|png|gif|svg)$/i,
loaders: [
"file-loader?hash=sha512&digest=hex&name=assets/img/[hash].[ext]",
"image-webpack-loader?bypassOnDebug&optipng.optimizationLevel=7&gifsicle.interlaced=false"
]
});
const envVars = {};
Object.keys(process.env).forEach((key) => {
envVars[`process.env.${key}`] = JSON.stringify(process.env[key]);
});
config.plugins = [
new webpack.DefinePlugin(envVars),
new StyleLintPlugin(),
new Dotenv({
path: "./.env"
}),
];
config.performance = {
hints: false
};
return config;
};
-72
View File
@@ -1,72 +0,0 @@
// Development config
const merge = require("webpack-merge");
const webpack = require("webpack");
const FaviconsWebpackPlugin = require("favicons-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const commonConfig = require("./common.js");
module.exports = function (env, argv) {
const base = commonConfig(env, argv);
base.mode = "development";
base.entry = [
"react-hot-loader/patch", // Activate HMR for React
"webpack-dev-server/client?http://0.0.0.0:3000", // Bundle the client for webpack-dev-server and connect to the provided endpoint
"webpack/hot/only-dev-server", // Bundle the client for hot reloading, only- means to only hot reload for successful updates
"./index.tsx" // The entry point of our app
];
base.devServer = {
hot: true, // Enable HMR on the server
historyApiFallback: true,
host: '0.0.0.0',
port: '3000',
allowedHosts: [
'.sik.party',
'.sahkoinsinoorikilta.fi',
],
};
base.devtool = "cheap-module-eval-source-map";
base.plugins = base.plugins.concat([
new FaviconsWebpackPlugin({
logo: "./assets/img/favicon.png",
prefix: "assets/icons/",
icons: {
android: false,
appleIcon: false,
appleStartup: false,
coast: false,
favicons: true,
firefox: false,
opengraph: false,
twitter: false,
yandex: false,
windows: false
}
}),
new webpack.HotModuleReplacementPlugin(), // Enable HMR globally
new webpack.NamedModulesPlugin(), // Prints more readable module names in the browser console on HMR updates
new HtmlWebpackPlugin({template: "index.html.ejs"}),
]);
base.module.rules.push({
test: /\.css$/,
use: ["style-loader", {loader: "css-loader", options: {importLoaders: 1}}, "postcss-loader"]
});
base.module.rules.push({
test: /\.scss$/,
loaders: [
"style-loader",
{loader: "css-loader", options: {importLoaders: 1}},
"postcss-loader",
{
loader: 'sass-loader',
options: {
// Prefer `dart-sass`
implementation: require('sass'),
},
},
]
});
return base;
};
-127
View File
@@ -1,127 +0,0 @@
// Production config
const path = require("path");
const merge = require("webpack-merge");
const resolve = path.resolve;
const FaviconsWebpackPlugin = require("favicons-webpack-plugin");
const nodeExternals = require('webpack-node-externals');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const WebpackCdnPlugin = require('webpack-cdn-plugin');
/* NOTE: This is a list of all routes that are prerendered for production use.
Please list all routes that contain search engine accessible content, i.e.,
stuff that you would like to find with a Google Search. */
const commonConfig = require("./common");
module.exports = function (env, argv) {
const base = commonConfig(env, argv);
base.mode = "production";
base.entry = "./index.tsx";
base.output = {
path: resolve(__dirname, "../../dist"),
publicPath: "/",
};
base.devtool = "source-map";
base.plugins.push(
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "assets/styles.css?[hash]",
}),
);
if (env.platform === 'server') {
base.entry = "./server/index.ts";
base.output.filename = 'js/server.js?[hash]';
base.target = 'node';
base.externals = [nodeExternals()];
base.module.rules.push({
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{loader: "css-loader", options: {importLoaders: 1}},
"postcss-loader"
],
});
base.module.rules.push({
test: /\.scss$/,
loaders: [
MiniCssExtractPlugin.loader,
{loader: "css-loader", options: {importLoaders: 1}},
"postcss-loader",
{
loader: 'sass-loader',
options: {
// Prefer `dart-sass`
implementation: require('sass'),
},
},
]
});
}
else if (env.platform === 'client') {
base.entry = './client/index.ts';
base.output.filename = 'js/client.js?[hash]';
base.target = 'web';
base.module.rules.push({
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{loader: "css-loader", options: {importLoaders: 1}},
"postcss-loader"
],
});
base.module.rules.push({
test: /\.scss$/,
loaders: [
MiniCssExtractPlugin.loader,
{loader: "css-loader", options: {importLoaders: 1}},
"postcss-loader",
{
loader: 'sass-loader',
options: {
// Prefer `dart-sass`
implementation: require('sass'),
},
},
]
});
base.plugins.push(
new HtmlWebpackPlugin({
template: "index.html.ejs",
}),
new FaviconsWebpackPlugin({
logo: "./assets/img/favicon.png",
prefix: "assets/icons/",
icons: {
android: true,
appleIcon: true,
appleStartup: true,
coast: true,
favicons: true,
firefox: true,
opengraph: true,
twitter: true,
yandex: true,
windows: true
}
}),
new WebpackCdnPlugin({
modules: [
{ name: 'react', var: 'React', path: `umd/react.${process.env.NODE_ENV}.min.js` },
{ name: 'react-dom', var: 'ReactDOM', path: `umd/react-dom.${process.env.NODE_ENV}.min.js` },
],
}),
);
}
return base;
};
-11
View File
@@ -1,11 +0,0 @@
version: '3'
services:
frontend:
build: .
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-frontend/frontend-prod
ports:
- "3000:3000"
environment:
API_URL: ${API_URL}
command: npm run start-prod
-17
View File
@@ -1,17 +0,0 @@
version: '3'
services:
frontend:
build: .
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-frontend
ports:
- "3000:3000"
environment:
API_URL: ${API_URL}
mock-backend:
build: .
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-frontend/frontend-mock-backend
ports:
- "1234:1234"
command: npm run mock-backend
+2
View File
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
+8
View File
@@ -0,0 +1,8 @@
module.exports = {
target: "serverless",
experimental: {
jsconfigPaths: true
},
env: {
}
}
+2426 -9804
View File
File diff suppressed because it is too large Load Diff
+6 -44
View File
@@ -20,7 +20,7 @@
"license": "MIT",
"homepage": "https://sik.ayy.fi",
"scripts": {
"build": "NODE_ENV=production npm-run-all build:client build:server",
"build": "next build",
"build:server": "webpack -p --config=configs/webpack/prod.js --env.platform=server",
"build:client": "webpack -p --config=configs/webpack/prod.js --env.platform=client",
"lint": "npm run lint:es && npm run lint:sass",
@@ -28,8 +28,8 @@
"lint:es:fix": "eslint --fix \"./src/**/*.{ts,tsx}\"",
"lint:sass": "stylelint \"./src/**/*.{scss,css}\"",
"start": "npm run start-dev",
"start-dev": "webpack-dev-server --config=configs/webpack/dev.js",
"serve": "node dist/js/server.js",
"start-dev": "next dev",
"serve": "next start",
"start-prod": "npm run build && npm run serve",
"test": "npm run test:e2e:verbose",
"test:e2e": "npm-run-all -p -r serve test:e2e:testcafe",
@@ -45,26 +45,14 @@
"devDependencies": {
"@types/jest": "24.0.22",
"@types/js-cookie": "2.2.4",
"@types/node": "10.14.7",
"@types/react": "16.8.18",
"@types/react-dom": "16.8.4",
"@types/react-helmet": "6.0.0",
"@types/react-jsonschema-form": "1.7.3",
"@types/react-router-dom": "5.1.5",
"@types/styled-components": "5.1.1",
"@typescript-eslint/eslint-plugin": "^4.8.2",
"@typescript-eslint/parser": "^4.8.2",
"babel-cli": "6.26.0",
"babel-core": "6.26.3",
"babel-loader": "7.1.5",
"babel-plugin-styled-components": "1.10.7",
"babel-preset-env": "1.7.0",
"babel-preset-minify": "0.4.3",
"babel-preset-react": "6.24.1",
"compression": "1.7.4",
"css-loader": "2.1.1",
"dotenv": "6.2.0",
"dotenv-webpack": "1.7.0",
"eslint": "^7.14.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.22.1",
@@ -74,51 +62,28 @@
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-standard": "^5.0.0",
"express": "4.17.0",
"favicons-webpack-plugin": "1.0.2",
"file-loader": "4.2.0",
"fs-extra": "6.0.1",
"helmet": "3.21.2",
"html-webpack-plugin": "3.2.0",
"husky": "1.3.1",
"image-webpack-loader": "6.0.0",
"mini-css-extract-plugin": "0.4.5",
"module-to-cdn": "3.1.2",
"morgan": "1.9.1",
"npm-run-all": "4.1.5",
"postcss-loader": "2.1.6",
"react": "16.8.6",
"react-addons-test-utils": "15.6.2",
"react-dom": "16.8.6",
"react-hot-loader": "4.8.8",
"sass": "1.29.0",
"sass-loader": "7.1.0",
"serve": "11.3.2",
"style-loader": "0.21.0",
"stylelint": "11.1.1",
"stylelint-config-recommended-scss": "4.0.0",
"stylelint-config-standard": "19.0.0",
"stylelint-scss": "3.12.1",
"stylelint-webpack-plugin": "1.0.3",
"testcafe": "1.6.1",
"testcafe-react-selectors": "2.1.0",
"ts-loader": "7.0.5",
"tsconfig-paths-webpack-plugin": "3.2.0",
"typescript": "3.9.5",
"typescript-plugin-styled-components": "1.4.4",
"uglifyjs-webpack-plugin": "1.3.0",
"webpack": "4.41.2",
"webpack-cdn-plugin": "3.2.0",
"webpack-cli": "3.3.10",
"webpack-dev-middleware": "3.7.2",
"webpack-dev-server": "3.11.0",
"webpack-merge": "4.2.2",
"webpack-node-externals": "1.7.2"
"typescript-plugin-styled-components": "1.4.4"
},
"dependencies": {
"axios": "0.19.0",
"date-fns": "2.0.0-alpha.27",
"js-cookie": "2.2.0",
"lodash": "4.17.20",
"next": "^10.0.3",
"normalize.css": "8.0.1",
"query-string": "6.5.0",
"react-beautiful-dnd": "10.1.1",
@@ -126,10 +91,7 @@
"react-jsonschema-form": "^1.8.1",
"react-markdown": "4.3.1",
"react-mde": "11.0.0",
"react-router-dom": "4.3.1",
"react-router-hash-link": "1.2.1",
"shortid": "2.2.14",
"styled-components": "5.1.1"
},
"postcss": {}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

-39
View File
@@ -1,39 +0,0 @@
$font: 'Montserrat', sans-serif;
$colors: (
// Name Color ?Hover?
dark-blue: 'dark-blue' #002d3a,
light-blue: 'light-blue' #bfdbd9,
white1: 'white1' #fff,
black1: 'black1' #000,
grey1: 'grey1' #d4d0c7,
grey2: 'grey2' #efece4,
orange1: 'orange1' #d57a2d,
orange2: 'orange2' #dd934e,
blue1: 'blue1' #57b2df,
light-turquoise: 'light-turquoise' #beddeb,
green1: 'green1' #c0dcd9,
sand: 'sand' #fdf9d7
);
@function color($label) {
$color: map-get($colors, $label);
@if $color {
@return nth($color, 2);
}
@warn 'No specified color for '#{$label}'; add your own to _colors.scss';
@return null;
}
@function hover($label) {
$color: map-get($colors, $label);
@if $color {
@return nth($color, 2);
}
@warn 'No specified hover color for '#{$label}'; add your own to _colors.scss';
@return lighten($color, 20%);
}
-9
View File
@@ -1,9 +0,0 @@
import React from "react";
import { BrowserRouter } from "react-router-dom";
import Routes from "../routes";
export default () => (
<BrowserRouter>
<Routes />
</BrowserRouter>
);
-7
View File
@@ -1,7 +0,0 @@
import React from "react";
import { hydrate } from "react-dom";
import App from "./App";
const elem = document.getElementById("root");
hydrate(React.createElement(App), elem);
+2 -7
View File
@@ -1,6 +1,5 @@
import React from "react";
import { Link } from "react-router-dom";
import { HashLink } from "react-router-hash-link";
import Link from "next/link";
interface AnchorProps extends React.HTMLAttributes<HTMLAnchorElement> {
to: string;
@@ -9,11 +8,7 @@ interface AnchorProps extends React.HTMLAttributes<HTMLAnchorElement> {
const Anchor: React.FC<AnchorProps> = ({ to, ...props }) => {
if (to.startsWith("/")) {
return (
<Link to={to} {...props} />
);
} else if (to.startsWith("#")) {
return (
<HashLink to={to} {...props} />
<Link href={to} {...props} />
);
}
else {
-38
View File
@@ -1,38 +0,0 @@
import React from "react";
import { render } from "react-dom";
import { BrowserRouter } from "react-router-dom";
import { AppContainer } from "react-hot-loader";
import Routes from "./routes";
import "./index.scss";
console.log("Using API URL: ", process.env.API_URL);
const rootEl = document.getElementById("root");
render(
<AppContainer>
<BrowserRouter>
<Routes />
</BrowserRouter>
</AppContainer>,
rootEl
);
// Hot Module Replacement API
declare const module: { hot: any };
if (module.hot) {
module.hot.accept("./routes", () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const NewRoutes = require("./routes").default;
render(
<AppContainer>
<BrowserRouter>
<NewRoutes />
</BrowserRouter>
</AppContainer>,
rootEl
);
});
}
-88
View File
@@ -1,88 +0,0 @@
import React from "react";
import { Helmet } from "react-helmet";
import { Event, getEvents } from "@models/Event";
import { Post, getFeed } from "@models/Feed";
import { StaticContext } from "@server/StaticContext";
import FrontPageView from "@views/FrontPage/FrontPageView";
interface FrontPageProps {
staticContext: StaticContext;
}
interface FrontPageState {
events: Event[];
feed: Post[];
}
class FrontPage extends React.Component<FrontPageProps, FrontPageState> {
constructor(props: FrontPageProps) {
super(props);
const { staticContext } = props;
if (staticContext) {
/* The static context is an object that manages promises when
rendering on the server. If staticContext exists, that means
we have to store all promises in it. Otherwise, operate
normally. See server/index.ts. */
if (staticContext.resolutions.getEvents) {
const events = staticContext.resolutions.getEvents as Event[];
const feed = staticContext.resolutions.getFeed as Post[];
this.state = {
events,
feed,
};
} else {
this.state = {
events: [],
feed: [],
};
const promiseEvents = this.fetchEvents();
const promiseFeed = this.fetchFeed();
staticContext.promises.getEvents = promiseEvents;
staticContext.promises.getFeed = promiseFeed;
}
} else {
this.state = {
events: [],
feed: [],
};
this.fetchEvents();
this.fetchFeed();
}
}
fetchEvents = () => {
const getEventsPromise = getEvents({
onlyNonPast: true,
limit: 4,
});
getEventsPromise.then(events => {
this.setState({
events,
});
});
return getEventsPromise;
}
fetchFeed = () => {
const getFeedPromise = getFeed();
getFeedPromise.then(feed => {
this.setState({
feed,
});
});
return getFeedPromise;
}
render() {
const { events, feed } = this.state;
return (
<>
<Helmet />
<FrontPageView events={events} feed={feed} />
</>
)
}
}
export default FrontPage;
+171
View File
@@ -0,0 +1,171 @@
import React from "react";
// import App from "next/app";
import type { AppProps /*, AppContext'*/} from "next/app"
import Head from "next/head";
import styled, { createGlobalStyle } from "styled-components";
import { colors } from "@theme/colors";
const fontFamily = "'Montserrat', sans-serif;"
const GlobalCommonStyles = createGlobalStyle`
* {
box-sizing: border-box;
}
html,
body {
font-family: ${fontFamily};
font-size: 12pt;
background-color: ${colors.darkBlue};
height: 100%;
padding: 0;
margin: auto !important;
}
p {
font-size: 1.2rem;
font-weight: 300;
&.large {
font-size: 2rem;
}
}
h1 {
font-size: 2.5rem;
font-weight: 200;
margin-block-start: 0;
margin-block-end: 0;
&.large {
font-size: 2.75rem;
}
}
h2 {
font-size: 1.2rem;
font-weight: 700;
letter-spacing: 0.1em;
margin-block-start: 0;
margin-block-end: 0;
text-transform: uppercase;
&.large {
font-size: 2rem;
}
}
h3 {
font-size: 1.5rem;
font-weight: 200;
margin-block-start: 0;
margin-block-end: 0;
&.large {
font-size: 2rem;
}
}
h4 {
font-size: 1rem;
font-weight: 700;
letter-spacing: 0.1em;
margin-block-start: 0;
margin-block-end: 0;
text-transform: uppercase;
&.large {
font-size: 2rem;
}
}
h5 {
font-size: 1.125rem;
font-weight: 600;
margin-block-start: 0;
margin-block-end: 0;
&.large {
font-size: 2.3rem;
}
}
h6 {
font-size: 0.8rem;
letter-spacing: 0.1em;
font-weight: 800;
margin-block-start: 0;
margin-block-end: 0;
text-transform: uppercase;
&.large {
font-size: 2rem;
}
}
/* TODO: List item style » */
li {
font-size: 1.2rem;
font-weight: 600;
&.large {
font-size: 2rem;
}
}
a {
text-decoration: underline;
color: ${colors.blue1};
}
a:hover {
text-decoration: none;
}
`;
const AppContainer = styled.div`
position: relative;
min-height: 100vh;
display: flex;
flex-flow: column nowrap;
color: ${colors.black};
background-color: ${colors.white};
`;
const Web20App = ({ Component, pageProps }: AppProps) => {
return (
<>
<Head>
<meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
<link href="https://fonts.googleapis.com/css?family=Montserrat:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,800,900&display=swap" rel="stylesheet" />
{/* <link rel="icon" href="/favicon.ico" /> */}
{/* <meta httpEquiv="X-UA-Compatible" content="IE=edge" /> */}
<meta name="viewport" content="width=device-width, initial-scale=1" />
{/* <meta name="description" content="SIK turns 100 years old!" /> */}
{/* <meta name="keywords" content="SIK100" /> */}
{/* <title>SIK100</title> */}
{/* <link rel="manifest" href="/manifest.json" /> */}
{/* <link rel="apple-touch-icon" href="/logo192.png" /> */}
{/* <meta name="theme-color" content={theme.colors.darkBlue2} /> */}
</Head>
<GlobalCommonStyles />
<AppContainer>
<Component {...pageProps} />
</AppContainer>
</>
)
}
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// export const getInitialProps = async (appContext: AppContext) => {
// // calls page's `getInitialProps` and fills `appProps.pageProps`
// const appProps = await App.getInitialProps(appContext);
// return { ...appProps }
// }
export default Web20App;
+43
View File
@@ -0,0 +1,43 @@
import React from "react";
import Document, {
Html, Head, Main, NextScript, DocumentContext,
} from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () => originalRenderPage({
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
render() {
const { styleTags } = this.props as any;
return (
<Html lang="fi">
<Head />
<body>
{styleTags}
<Main />
<NextScript />
</body>
</Html>
);
}
}
+25
View File
@@ -0,0 +1,25 @@
import React from "react";
import { NextPage, GetServerSideProps } from "next";
import { getEvents } from "@models/Event";
import { getFeed } from "@models/Feed";
import FrontPageView from "@views/FrontPage/FrontPageView";
const FrontPage: NextPage<React.ComponentProps<typeof FrontPageView>> = (props) => (
<FrontPageView {...props} />
);
export const getServerSideProps: GetServerSideProps = async () => {
const events = await getEvents({
onlyNonPast: true,
limit: 4,
});
const feed = await getFeed();
return {
props: {
events,
feed,
},
};
};
export default FrontPage;
+1 -1
View File
@@ -1,7 +1,7 @@
import React from "react";
import { Switch, Route } from "react-router-dom";
import { Helmet } from "react-helmet";
import FrontPage from "./pages/FrontPage";
import FrontPage from "./pages";
import GuildPage from "./pages/GuildPage";
import NotFoundPage from "./pages/NotFoundPage";
import CommonPage from "./pages/CommonPage";
-9
View File
@@ -1,9 +0,0 @@
import React from "react";
import { StaticRouter } from "react-router-dom";
import Routes from "../routes";
export default ({ url, context }) => (
<StaticRouter context={context} location={url}>
<Routes />
</StaticRouter>
);
-4
View File
@@ -1,4 +0,0 @@
export interface StaticContext {
resolutions: { [key: string]: any };
promises: { [key: string]: Promise<any> };
}
-92
View File
@@ -1,92 +0,0 @@
import React from "react";
import { Helmet } from "react-helmet";
import express from "express";
import { renderToString } from "react-dom/server";
import morgan from "morgan";
import helmet from "helmet";
import compression from "compression";
import dotenv from "dotenv";
dotenv.config();
console.log(`API_URL: ${process.env.API_URL}`);
import App from "./App";
import fs from "fs";
import path from "path";
import { StaticContext } from "./StaticContext";
import { ServerStyleSheet } from "styled-components";
const port = 3000;
const server = express();
const indexHtml = fs.readFileSync(path.resolve("./dist/index.html"), "utf-8");
const html = (body, styles, title, meta) => {
const withBody = indexHtml.replace("<div id=\"root\"></div>", `<div id="root">${body}</div>`);
const withHead = withBody.replace("<head>", `<head>${title}${meta}${styles}`);
return withHead;
};
server.use(morgan("short"));
server.use(helmet());
server.use(helmet.hidePoweredBy());
server.use(compression());
server.use("/assets", express.static("dist/assets"));
server.use("/js", express.static("dist/js"));
server.get("*", async (req, res) => {
const context: StaticContext = {
resolutions: {}, promises: {},
};
// Styled-components Step 1: Create an instance of ServerStyleSheet
const sheet = new ServerStyleSheet();
// Styled-components Step 2: Retrieve styles from components in the page
const firstPassRenderResult = renderToString(
sheet.collectStyles(
React.createElement(App, { url: req.url, context })
)
);
const promiseKeys = Object.keys(context.promises);
let result: string;
if (promiseKeys.length === 0) {
/* No promises to resolve on the first pass. Render html normally. */
result = firstPassRenderResult;
} else {
/* Some promises have to be resolved before rendering the page. */
const promiseEntries = promiseKeys.map(async key => {
const promise = context.promises[key];
return { key, value: await promise };
});
/* Resolve all promises. */
const awaitedEntries = await Promise.all(promiseEntries);
/* Store all resolutions in the context. */
awaitedEntries.forEach(entry => {
context.resolutions[entry.key] = entry.value;
});
/* Render a second time with all resolved data. */
// Styled-components Step 2: Retrieve styles from components in the page
const secondPassRenderResult = renderToString(
sheet.collectStyles(
React.createElement(App, { url: req.url, context })
)
);
result = secondPassRenderResult;
}
// Styled-components Step 3: Extract the styles as <style> tags
const styles = sheet.getStyleTags();
const head = Helmet.rewind();
res.send(
html(result, styles, head.title, head.meta)
);
});
server.listen(3000, () => console.log("React SSR express server listening on port 3000!"));
+19 -3
View File
@@ -10,10 +10,15 @@
// "resolveJsonModule": true,
"module": "commonjs",
"target": "es5",
"jsx": "react",
"lib": ["es5", "es6", "es7", "dom"],
"jsx": "preserve",
"lib": [
"es5",
"es6",
"es7",
"dom"
],
"typeRoots": [
"./types",
"./types"
],
"types": [
"node",
@@ -55,9 +60,20 @@
"src/utils/*"
]
},
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
},
"include": [
"./src/**/*",
"./types/**/*"
],
"exclude": [
"node_modules"
]
}