Adding Redux to project, updating to version 0.1.0
This commit is contained in:
parent
d4f2474436
commit
ec020257e0
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,6 +7,7 @@ build
|
|||||||
src/server/main
|
src/server/main
|
||||||
src/server/react-server
|
src/server/react-server
|
||||||
src/server/web/dist
|
src/server/web/dist
|
||||||
src/server/web/utils
|
src/server/web/conversion
|
||||||
|
src/server/web/getstate
|
||||||
#cypress
|
#cypress
|
||||||
cypress/videos
|
cypress/videos
|
@ -1,6 +1,6 @@
|
|||||||
# Create React SSR
|
# Create React SSR
|
||||||
|
|
||||||
This project aims to have a starter kit for creating a new React app with Server Side Rendering and tools that generally go along with it.
|
This project aims to have a starter kit for creating a new React app with Server Side Rendering with a backend in go and tools that generally go along with it.
|
||||||
|
|
||||||
It is not a project like create-react-app, create-react-app is used as a starter kit that handles all your scripts underneath, this is a project for developers who want more control over their application.
|
It is not a project like create-react-app, create-react-app is used as a starter kit that handles all your scripts underneath, this is a project for developers who want more control over their application.
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ Typescript | 5.2.2
|
|||||||
## Setup
|
## Setup
|
||||||
To create a new project run in the terminal:
|
To create a new project run in the terminal:
|
||||||
```
|
```
|
||||||
npx @aleleba/create-react-ssr app-name
|
npx @aleleba/create-react-go-ssr app-name
|
||||||
```
|
```
|
||||||
Then run:
|
Then run:
|
||||||
```
|
```
|
||||||
|
16513
package-lock.json
generated
Normal file
16513
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@aleleba/create-react-go-ssr",
|
"name": "@aleleba/create-react-go-ssr",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"description": "Starter Kit of server side render of react",
|
"description": "Starter Kit of server side render of react",
|
||||||
"bin": "./bin/cli.js",
|
"bin": "./bin/cli.js",
|
||||||
"main": "src/server/index",
|
"main": "src/server/index",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack watch --config webpack.config.ts",
|
"start-frontend:dev": "webpack watch --config webpack.config.ts",
|
||||||
"buildTsxToString": "webpack --config webpack.config.convert.ts",
|
"start-server:dev": "cd src/server && go run main.go",
|
||||||
"start:dev": "rm -rf build && webpack --mode=development --config webpack.config.dev.server.ts",
|
|
||||||
"start:dev-win": "(if exist build rmdir /s /Q build) && webpack --mode=development --config webpack.config.dev.server.ts",
|
|
||||||
"build": "webpack-cli --config webpack.config.ts",
|
|
||||||
"lint": "eslint ./ --ext .js --ext .ts --ext .jsx --ext .tsx",
|
"lint": "eslint ./ --ext .js --ext .ts --ext .jsx --ext .tsx",
|
||||||
"lint:fix": "eslint ./ --ext .js --ext .ts --ext .jsx --ext .tsx --fix",
|
"lint:fix": "eslint ./ --ext .js --ext .ts --ext .jsx --ext .tsx --fix",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
27
src/__mocks__/ProviderMock.tsx
Normal file
27
src/__mocks__/ProviderMock.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { Router } from 'react-router-dom';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
|
import initialStateReducer from '../frontend/reducers/initialState';
|
||||||
|
import setStore from '../frontend/setStore';
|
||||||
|
|
||||||
|
export const ProviderMock = ({ children, initialState }: { children: unknown, initialState?: unknown}) => {
|
||||||
|
let initialStateMock = initialStateReducer;
|
||||||
|
|
||||||
|
if(initialState !== undefined){
|
||||||
|
initialStateMock = initialState as unknown as typeof initialStateReducer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const history = createMemoryHistory();
|
||||||
|
const store = setStore({ initialState: initialStateMock });
|
||||||
|
|
||||||
|
return(
|
||||||
|
<Provider store={store}>
|
||||||
|
<Router location={history.location} navigator={history}>
|
||||||
|
{children as JSX.Element}
|
||||||
|
</Router>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProviderMock;
|
2
src/__mocks__/fileMock.ts
Normal file
2
src/__mocks__/fileMock.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export const fileMock = '';
|
||||||
|
module.exports = fileMock;
|
1
src/__mocks__/index.ts
Normal file
1
src/__mocks__/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './ProviderMock';
|
@ -3,7 +3,7 @@ import PrincipalRoutes from './PrincipalRoutes';
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ws = new WebSocket('wss://xs70kvlc-3000.use.devtunnels.ms/ws');
|
const ws = new WebSocket('wss://nmr4jbx8-3000.use.devtunnels.ms/ws');
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
if (event.data === 'reload') {
|
if (event.data === 'reload') {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
|
@ -1,25 +1,36 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './InitialComponent.scss';
|
import './InitialComponent.scss';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
const InitialComponent = () => (
|
const InitialComponent = ({ hello }: { hello: string }) => {
|
||||||
<div className="App">
|
|
||||||
<header className="App-header">
|
|
||||||
<img src="assets/img/logo.svg" className="App-logo" alt="logo" />
|
|
||||||
<p>
|
|
||||||
Edit <code>src/frontend/InitialComponent.jsx</code> and save to reload.
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
className="App-link"
|
|
||||||
href="https://reactjs.org"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Learn React
|
|
||||||
</a>
|
|
||||||
<Link className="App-link" to="/other-component">Other Component</Link>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default InitialComponent;
|
return(
|
||||||
|
<div className="App">
|
||||||
|
<header className="App-header">
|
||||||
|
<img src="assets/img/logo.svg" className="App-logo" alt="logo" />
|
||||||
|
<p>This is the text from the store of redux: <strong>{hello}</strong></p>
|
||||||
|
<p>
|
||||||
|
Edit <code>src/frontend/InitialComponent.jsx</code> and save to reload.
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
className="App-link"
|
||||||
|
href="https://reactjs.org"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Learn React
|
||||||
|
</a>
|
||||||
|
<Link className="App-link" to="/other-component">Other Component</Link>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => {
|
||||||
|
return {
|
||||||
|
hello: state.testReducer.hello
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(InitialComponent);
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
//Redux
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import setStore from '../setStore';
|
||||||
|
import initialState from '../reducers/initialState';
|
||||||
import { renderToString } from 'react-dom/server';
|
import { renderToString } from 'react-dom/server';
|
||||||
import { StaticRouter } from 'react-router-dom/server';
|
import { StaticRouter } from 'react-router-dom/server';
|
||||||
import App from '../components/App';
|
import App from '../components/App';
|
||||||
|
|
||||||
const url = process.argv[2];
|
const url = process.argv[2];
|
||||||
|
|
||||||
|
const store = setStore({ initialState });
|
||||||
|
|
||||||
const render = () => {
|
const render = () => {
|
||||||
return renderToString(
|
return renderToString(
|
||||||
<StaticRouter location={`${url}`} >
|
<Provider store={store}>
|
||||||
<App />
|
<StaticRouter location={`${url}`} >
|
||||||
</StaticRouter>
|
<App />
|
||||||
|
</StaticRouter>
|
||||||
|
</Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
5
src/frontend/getPreloadedState/getPreloadedState.ts
Normal file
5
src/frontend/getPreloadedState/getPreloadedState.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import setStore from '../setStore';
|
||||||
|
import initialState from '../reducers/initialState';
|
||||||
|
const store = setStore({ initialState });
|
||||||
|
const preloadedState = store.getState();
|
||||||
|
console.log(preloadedState);
|
@ -10,7 +10,7 @@ import { config } from '../../config';
|
|||||||
|
|
||||||
import './styles/global.scss';
|
import './styles/global.scss';
|
||||||
import App from './components/App';
|
import App from './components/App';
|
||||||
// import serviceWorkerRegistration from '../../serviceWorkerRegistration';
|
import serviceWorkerRegistration from '../../serviceWorkerRegistration';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@ -72,10 +72,10 @@ ENV === 'production' && hydrateRoot(container,
|
|||||||
</Provider>
|
</Provider>
|
||||||
); */
|
); */
|
||||||
|
|
||||||
/* if((ENV) && (ENV === 'production')){
|
if((ENV) && (ENV === 'production')){
|
||||||
serviceWorkerRegistration();
|
serviceWorkerRegistration();
|
||||||
} */
|
}
|
||||||
|
|
||||||
/* if(module.hot){
|
/*if(module.hot){
|
||||||
module.hot.accept();
|
module.hot.accept();
|
||||||
} */
|
}*/
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
const initialState = {};
|
import { IInitialState } from './';
|
||||||
|
const initialState: IInitialState = {};
|
||||||
export default initialState;
|
export default initialState;
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func LoadEnv() {
|
func LoadEnv() {
|
||||||
err := godotenv.Load(".env")
|
err := godotenv.Load("../../.env")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error loading .env file")
|
log.Fatal("Error loading .env file")
|
||||||
}
|
}
|
||||||
|
37
src/server/utils/getPreloadedState.go
Normal file
37
src/server/utils/getPreloadedState.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPreloadedState() string {
|
||||||
|
// Set the path to the jsxToString.js file
|
||||||
|
jsFilePath := "./web/getstate/getPreloadedState.js"
|
||||||
|
|
||||||
|
// Create the command to run Node.js with the jsxToString.js file
|
||||||
|
cmd := exec.Command("node", jsFilePath)
|
||||||
|
|
||||||
|
// Get a pipe to the standard input of the Node.js process
|
||||||
|
stdin, err := cmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the URL parameter to the standard input of the Node.js process
|
||||||
|
//fmt.Fprintf(stdin, "%s\n", url)
|
||||||
|
|
||||||
|
// Close the standard input pipe
|
||||||
|
stdin.Close()
|
||||||
|
|
||||||
|
// Get the output and error of the Node.js process
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return string(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the output of the Node.js process
|
||||||
|
return string(output)
|
||||||
|
}
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
func JsxToString(url string) string {
|
func JsxToString(url string) string {
|
||||||
// Set the path to the jsxToString.js file
|
// Set the path to the jsxToString.js file
|
||||||
jsFilePath := "./web/utils/tsxToString.js"
|
jsFilePath := "./web/conversion/tsxToString.js"
|
||||||
|
|
||||||
// Create the command to run Node.js with the jsxToString.js file
|
// Create the command to run Node.js with the jsxToString.js file
|
||||||
cmd := exec.Command("node", jsFilePath, url)
|
cmd := exec.Command("node", jsFilePath, url)
|
||||||
|
@ -32,6 +32,7 @@ func RegisterHandlers(e *echo.Echo, paths []string) {
|
|||||||
//return c.File(filePath)
|
//return c.File(filePath)
|
||||||
url := c.Request().URL.String()
|
url := c.Request().URL.String()
|
||||||
component := utils.JsxToString(url)
|
component := utils.JsxToString(url)
|
||||||
|
preloadedState := utils.GetPreloadedState();
|
||||||
html := `<!DOCTYPE html>
|
html := `<!DOCTYPE html>
|
||||||
<html lang="es">
|
<html lang="es">
|
||||||
<head>
|
<head>
|
||||||
@ -47,9 +48,9 @@ func RegisterHandlers(e *echo.Echo, paths []string) {
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">`+ component +`</div>
|
<div id="app">`+ component +`</div>
|
||||||
<!-- <script>
|
<script>
|
||||||
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
|
window.__PRELOADED_STATE__ = JSON.stringify(`+ preloadedState+`).replace(/</g, '\\u003c')
|
||||||
</script> -->
|
</script>
|
||||||
<script src="assets/app-frontend.js" type="text/javascript"></script>
|
<script src="assets/app-frontend.js" type="text/javascript"></script>
|
||||||
<script src="assets/vendor-vendors.js" type="text/javascript"></script>
|
<script src="assets/vendor-vendors.js" type="text/javascript"></script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import dotenv from 'dotenv'
|
||||||
import { config as envConfig } from './config';
|
import { config as envConfig } from './config';
|
||||||
import webpack from 'webpack';
|
import webpack from 'webpack';
|
||||||
import CompressionWebpackPlugin from 'compression-webpack-plugin';
|
import CompressionWebpackPlugin from 'compression-webpack-plugin';
|
||||||
@ -15,7 +16,8 @@ import { resolveTsAliases } from 'resolve-ts-aliases';
|
|||||||
const ROOT_DIR = path.resolve(__dirname);
|
const ROOT_DIR = path.resolve(__dirname);
|
||||||
const resolvePath = (...args) => path.resolve(ROOT_DIR, ...args);
|
const resolvePath = (...args) => path.resolve(ROOT_DIR, ...args);
|
||||||
const BUILD_DIR = resolvePath(__dirname + '/src/server/web/dist');
|
const BUILD_DIR = resolvePath(__dirname + '/src/server/web/dist');
|
||||||
const BUILD_DIR_CONVERSION = resolvePath(__dirname + '/src/server/web/utils');
|
const BUILD_DIR_CONVERSION = resolvePath(__dirname + '/src/server/web/conversion');
|
||||||
|
const BUILD_DIR_GET_STATE = resolvePath(__dirname + '/src/server/web/getstate');
|
||||||
//const { InjectManifest } = require('workbox-webpack-plugin');
|
//const { InjectManifest } = require('workbox-webpack-plugin');
|
||||||
//const nodeExternals = require('webpack-node-externals');
|
//const nodeExternals = require('webpack-node-externals');
|
||||||
const alias = resolveTsAliases(path.resolve('tsconfig.json'));
|
const alias = resolveTsAliases(path.resolve('tsconfig.json'));
|
||||||
@ -42,6 +44,17 @@ if(fs.existsSync(`${ROOT_DIR}/public/img`)){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// call dotenv and it will return an Object with a parsed key
|
||||||
|
const env = dotenv.config().parsed;
|
||||||
|
|
||||||
|
// reduce it to a nice object, the same as before
|
||||||
|
const envKeys = Object.keys(dotenv.config({}).parsed || {}).reduce((prev, next) => {
|
||||||
|
if (dotenv.config().parsed && env && env[next]) {
|
||||||
|
prev[`process.env.${next}`] = JSON.stringify(env[next]);
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
|
||||||
const configReact = {
|
const configReact = {
|
||||||
entry: {
|
entry: {
|
||||||
frontend: `${ROOT_DIR}/src/frontend/index.tsx`,
|
frontend: `${ROOT_DIR}/src/frontend/index.tsx`,
|
||||||
@ -112,6 +125,7 @@ const configReact = {
|
|||||||
}),
|
}),
|
||||||
new ESLintPlugin(),
|
new ESLintPlugin(),
|
||||||
new webpack.EnvironmentPlugin({
|
new webpack.EnvironmentPlugin({
|
||||||
|
envKeys,
|
||||||
...envConfig,
|
...envConfig,
|
||||||
}),
|
}),
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
@ -200,9 +214,67 @@ const configTSXConversion = {
|
|||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new ESLintPlugin(),
|
new ESLintPlugin(),
|
||||||
new webpack.EnvironmentPlugin({
|
],
|
||||||
...envConfig,
|
optimization: {
|
||||||
|
minimize: true,
|
||||||
|
minimizer: [
|
||||||
|
new CssMinimizerPlugin(),
|
||||||
|
new TerserPlugin(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const configGetPreloadedState = {
|
||||||
|
entry: {
|
||||||
|
getPreloadedState: `${ROOT_DIR}/src/frontend/getPreloadedState/getPreloadedState.ts`,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: BUILD_DIR_GET_STATE,
|
||||||
|
//filename: 'assets/app-[name]-[fullhash].js',
|
||||||
|
filename: '[name].js',
|
||||||
|
publicPath: envConfig.PUBLIC_URL,
|
||||||
|
globalObject: 'this',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.jsx','.ts','.tsx', '.json'],
|
||||||
|
alias
|
||||||
|
},
|
||||||
|
mode: 'production',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(js|jsx|ts|tsx)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'babel-loader',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(css|sass|scss)$/,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
'css-loader',
|
||||||
|
'sass-loader',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CompressionWebpackPlugin({
|
||||||
|
test: /\.(js|css)$/,
|
||||||
|
filename: '[path][base].gz',
|
||||||
}),
|
}),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
//filename: 'assets/[name]-[fullhash].css',
|
||||||
|
filename: '[name].css',
|
||||||
|
}),
|
||||||
|
new CleanWebpackPlugin({
|
||||||
|
cleanOnceBeforeBuildPatterns: [
|
||||||
|
'**/*',
|
||||||
|
'!server/**',
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new ESLintPlugin(),
|
||||||
],
|
],
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: true,
|
minimize: true,
|
||||||
@ -213,4 +285,4 @@ const configTSXConversion = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default [configReact, configTSXConversion];
|
export default [configReact, configTSXConversion, configGetPreloadedState];
|
||||||
|
Loading…
Reference in New Issue
Block a user