diff --git a/README.md b/README.md
index f9a8f45..8bd8126 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# template-for-react-ts-ssr-hmr
-Template for a project with React, TypeScript, SSR, HMR. Template imply using CSS Modules and Stylus. But it can be easily changed.
+Template for a project with React, TypeScript, SSR, and HMR. The template implies the use of CSS Modules and Stylus.
-## Quick start:
+## Quick start:
To copy the template in an empty local folder without creating of a repo or in an existing repo use the following:
@@ -10,7 +10,7 @@ To copy the template in an empty local folder without creating of a repo or in a
npx degit Kallenju/template-for-react-ts-ssr-hmr
```
-Install packages and run development servers:
+Install packages and run development servers:
```shell
npm install
@@ -18,10 +18,39 @@ npm install
npm run dev
```
+## Structure of the template
+
+### Structure of the source folder (`./src`)
+
+- **assets**
+ - **styles**
+ - **body**
+ - **html**
+ - **root**
+- **client**
+- **server**
+- **shared**
+ - **Header**
+ - **Layout**
+- **types**
+
+## How it works
+
+`./src` contains `App.tsx`, which is the entry point of the React app. It imports components from `./src/shared` and global styles from `./assets/styles/`. `App.tsx` is in turn imported be server and client files in `./src/server/` and `./src/client/`, respectively.
+
+The `app.get()` method specifies a callback function that will render to string React component from `App.tsx` and past it in a HTML template, whenever there is an HTTP GET request with a path ('/') relative to the site root. HTML template are in `./src/server`.
+
+After the `load` event is fired, the client code `hydrate` obtained React component.
+
## What is used in the template?
-### Hot module replacement
+### Hot Module Replacement
+- express server
- react-refresh-webpack-plugin
- React Fast Refresh
- Webpack modules and plugins (webpack-dev-middleware, webpack-hot-middleware, webpack.HotModuleReplacementPlugin)
+
+### Server-Side Rendering
+
+- express server
diff --git a/config/webpack.client.config.js b/config/webpack.client.config.js
index 7300d7f..d56b009 100644
--- a/config/webpack.client.config.js
+++ b/config/webpack.client.config.js
@@ -5,7 +5,10 @@ const webpack = require('webpack');
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
-const { NODE_ENV } = globalThis.process.env;
+const { NODE_ENV } = process.env;
+const MODULE_CODE_REGEXP = /\.[tj]sx?$/;
+const MODULE_STYLES_REGEXP = /\.module\.styl$/;
+const GLOBAL_STYLES_REGEXP = /\.global\.styl$/;
module.exports = {
entry: [
@@ -21,7 +24,7 @@ module.exports = {
module: {
rules: [
{
- test: /\.[tj]sx?$/,
+ test: MODULE_CODE_REGEXP,
use: [
{
loader: 'babel-loader',
@@ -29,7 +32,8 @@ module.exports = {
],
},
{
- test: /\.styl$/,
+ test: MODULE_STYLES_REGEXP,
+ exclude: GLOBAL_STYLES_REGEXP,
use: [
{
loader: 'style-loader',
@@ -58,6 +62,20 @@ module.exports = {
},
],
},
+ {
+ test: GLOBAL_STYLES_REGEXP,
+ use: [
+ {
+ loader: 'css-loader',
+ },
+ {
+ loader: 'postcss-loader',
+ },
+ {
+ loader: 'stylus-loader',
+ },
+ ],
+ },
],
},
diff --git a/package.json b/package.json
index 98b88d6..333bf57 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "template-for-react-ts-ssr-hmr",
- "version": "1.0.1",
- "description": "Template for a project with React, TypeScript, SSR, HMR. Template imply using CSS Modules and Stylus. But it can be easily changed.",
+ "version": "1.1.0",
+ "description": "Template for a project with React, TypeScript, SSR, HMR. The template implies the use of CSS Modules and Stylus.",
"repository": {
"type": "git",
"url": "https://github.com/Kallenju/template-for-react-ts-ssr-hmr.git"
diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 0000000..c3bb973
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import { Layout } from './shared/Layout';
+import { Header } from './shared/Header';
+import './assets/styles/main.global.styl';
+
+export default function App(): React.ReactElement {
+ return (
+
+
+
+ );
+}
diff --git a/src/assets/styles/body/body.styl b/src/assets/styles/body/body.styl
new file mode 100644
index 0000000..0463a14
--- /dev/null
+++ b/src/assets/styles/body/body.styl
@@ -0,0 +1,8 @@
+body
+ margin 0
+ min-width 320px
+ font-family --font-family-primary, sans-serif
+ font-size --font-size_sm
+ font-weight --font-weight_xs
+ line-height --line-height_xs
+ background-color --color-white
diff --git a/src/assets/styles/body/index.styl b/src/assets/styles/body/index.styl
new file mode 100644
index 0000000..b995e1c
--- /dev/null
+++ b/src/assets/styles/body/index.styl
@@ -0,0 +1 @@
+@import 'body'
diff --git a/src/assets/styles/html/html.styl b/src/assets/styles/html/html.styl
new file mode 100644
index 0000000..b7dba77
--- /dev/null
+++ b/src/assets/styles/html/html.styl
@@ -0,0 +1,12 @@
+html
+ box-sizing border-box
+ font-size 0.625em
+
+*
+ box-sizing inherit
+
+ &::before
+ box-sizing inherit
+
+ &::after
+ box-sizing inherit
diff --git a/src/assets/styles/html/index.styl b/src/assets/styles/html/index.styl
new file mode 100644
index 0000000..6bc98a6
--- /dev/null
+++ b/src/assets/styles/html/index.styl
@@ -0,0 +1 @@
+@import 'html'
diff --git a/src/assets/styles/main.global.styl b/src/assets/styles/main.global.styl
new file mode 100644
index 0000000..caab3c6
--- /dev/null
+++ b/src/assets/styles/main.global.styl
@@ -0,0 +1,3 @@
+@import './root'
+@import './html'
+@import './body'
diff --git a/src/assets/styles/root/index.styl b/src/assets/styles/root/index.styl
new file mode 100644
index 0000000..0c40890
--- /dev/null
+++ b/src/assets/styles/root/index.styl
@@ -0,0 +1 @@
+@import 'root';
diff --git a/src/assets/styles/root/root.styl b/src/assets/styles/root/root.styl
new file mode 100644
index 0000000..eb22800
--- /dev/null
+++ b/src/assets/styles/root/root.styl
@@ -0,0 +1,52 @@
+--container-width = 1400px
+--container-margins = 55px
+--container-margins_md = 31px
+--container-margins_sm = 27px
+--container-margins_xs = 21px
+--font-family-primary = 'Roboto'
+--font-family-secondary = 'ttfirsneue'
+--font-size_xxs = 1.20rem
+--font-size_xs = 1.40rem
+--font-size_sm = 1.60rem
+--font-size_md = 1.80rem
+--font-size_lg = 2.00rem
+--font-size_xl = 2.40rem
+--font-size_xxl = 3.40rem
+--line-height_xs = 1.04
+--line-height_sm = 1.17
+--line-height_md = 1.29
+--line-height_lg = 1.38
+--line-height_xl = 1.75
+--font-weight_xs = 400
+--font-weight_sm = 500
+--font-weight_md = 600
+--font-weight_lg = 700
+--font-weight_xl = 800
+--warning-color-primary = #ff3030
+--success-color-primary = #7cbc4a
+--brend-color = #ff6e30
+--background-color-primary = #ff5c00
+--background-color-secondary = #202020
+--color-focus-primary = #ff6e30
+--color-hover-primary = #bc572b
+--color-active-primary = #ce4307
+--border-color-primary = #ff6e30
+--border-color-secondary = #ce4307
+--text-color-primary = #fff
+--text-color-secondary = #202020
+--form-text-color-primary = #202020
+--color-dark-grey = #202020
+--color-middle-grey = #999
+--color-light-grey = #e9e9e9
+--color-white = #fff
+--color-black = #000
+--transition-time_slow = 0.210s
+--transition-time_fast = 0.100s
+
+--breakpoint_xs = 40em
+--breakpoint_sm = 56em
+--breakpoint_md = 66em
+--breakpoint_lg = 82em
+--grid-columns = 12
+--grid-gap = 3.00rem
+--link_css-effects = 'plain'
diff --git a/src/client/index.tsx b/src/client/index.tsx
index 7006d2f..8077a07 100644
--- a/src/client/index.tsx
+++ b/src/client/index.tsx
@@ -1,11 +1,11 @@
import React from 'react';
import ReactDom from 'react-dom/client';
-import Header from '../shared/Header';
+import App from '../App';
window.addEventListener('load', () => {
const container: Element | null = document.querySelector('#react_root');
if (container) {
- ReactDom.hydrateRoot(container, );
+ ReactDom.hydrateRoot(container, );
}
});
diff --git a/src/server/server.tsx b/src/server/server.tsx
index 37e23fe..78ef8f7 100644
--- a/src/server/server.tsx
+++ b/src/server/server.tsx
@@ -1,7 +1,7 @@
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
-import Header from '../shared/Header';
+import App from '../App';
import indexTemplate from './indexTemplate';
const app = express();
@@ -9,7 +9,7 @@ const app = express();
app.use('/static', express.static('./dist/client'));
app.get('/', (req, res) => {
- res.send(indexTemplate(ReactDOMServer.renderToString()));
+ res.send(indexTemplate(ReactDOMServer.renderToString()));
});
app.listen(3000);
diff --git a/src/shared/Header.tsx b/src/shared/Header/Header.tsx
similarity index 87%
rename from src/shared/Header.tsx
rename to src/shared/Header/Header.tsx
index d32ded6..7d88aa5 100644
--- a/src/shared/Header.tsx
+++ b/src/shared/Header/Header.tsx
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import styles from './header.module.styl';
-export default function Header(): React.ReactElement {
+export function Header(): React.ReactElement {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
diff --git a/src/shared/header.module.styl b/src/shared/Header/header.module.styl
similarity index 100%
rename from src/shared/header.module.styl
rename to src/shared/Header/header.module.styl
diff --git a/src/shared/Header/index.ts b/src/shared/Header/index.ts
new file mode 100644
index 0000000..266dec8
--- /dev/null
+++ b/src/shared/Header/index.ts
@@ -0,0 +1 @@
+export * from './Header';
diff --git a/src/shared/Layout/Layout.tsx b/src/shared/Layout/Layout.tsx
new file mode 100644
index 0000000..c409999
--- /dev/null
+++ b/src/shared/Layout/Layout.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+interface ILayoutProps {
+ children?: React.ReactNode;
+}
+
+export function Layout({ children }: ILayoutProps): React.ReactElement {
+ return {children};
+}
diff --git a/src/shared/Layout/index.ts b/src/shared/Layout/index.ts
new file mode 100644
index 0000000..9877e7f
--- /dev/null
+++ b/src/shared/Layout/index.ts
@@ -0,0 +1 @@
+export * from './Layout';
diff --git a/src/shared/Layout/layout.module.styl b/src/shared/Layout/layout.module.styl
new file mode 100644
index 0000000..e69de29
diff --git a/src/custom.d.ts b/src/types/custom.d.ts
similarity index 100%
rename from src/custom.d.ts
rename to src/types/custom.d.ts