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/react-server
|
||||
src/server/web/dist
|
||||
src/server/web/utils
|
||||
src/server/web/conversion
|
||||
src/server/web/getstate
|
||||
#cypress
|
||||
cypress/videos
|
@ -1,6 +1,6 @@
|
||||
# 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.
|
||||
|
||||
@ -16,7 +16,7 @@ Typescript | 5.2.2
|
||||
## Setup
|
||||
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:
|
||||
```
|
||||
|
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",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"description": "Starter Kit of server side render of react",
|
||||
"bin": "./bin/cli.js",
|
||||
"main": "src/server/index",
|
||||
"scripts": {
|
||||
"start": "webpack watch --config webpack.config.ts",
|
||||
"buildTsxToString": "webpack --config webpack.config.convert.ts",
|
||||
"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",
|
||||
"start-frontend:dev": "webpack watch --config webpack.config.ts",
|
||||
"start-server:dev": "cd src/server && go run main.go",
|
||||
"lint": "eslint ./ --ext .js --ext .ts --ext .jsx --ext .tsx",
|
||||
"lint:fix": "eslint ./ --ext .js --ext .ts --ext .jsx --ext .tsx --fix",
|
||||
"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 = () => {
|
||||
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) => {
|
||||
if (event.data === 'reload') {
|
||||
window.location.reload();
|
||||
|
@ -1,25 +1,36 @@
|
||||
import React from 'react';
|
||||
import './InitialComponent.scss';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const InitialComponent = () => (
|
||||
<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>
|
||||
);
|
||||
const InitialComponent = ({ hello }: { hello: string }) => {
|
||||
|
||||
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';
|
||||
//Redux
|
||||
import { Provider } from 'react-redux';
|
||||
import setStore from '../setStore';
|
||||
import initialState from '../reducers/initialState';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { StaticRouter } from 'react-router-dom/server';
|
||||
import App from '../components/App';
|
||||
|
||||
const url = process.argv[2];
|
||||
|
||||
const store = setStore({ initialState });
|
||||
|
||||
const render = () => {
|
||||
return renderToString(
|
||||
<StaticRouter location={`${url}`} >
|
||||
<App />
|
||||
</StaticRouter>
|
||||
<Provider store={store}>
|
||||
<StaticRouter location={`${url}`} >
|
||||
<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 App from './components/App';
|
||||
// import serviceWorkerRegistration from '../../serviceWorkerRegistration';
|
||||
import serviceWorkerRegistration from '../../serviceWorkerRegistration';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -72,10 +72,10 @@ ENV === 'production' && hydrateRoot(container,
|
||||
</Provider>
|
||||
); */
|
||||
|
||||
/* if((ENV) && (ENV === 'production')){
|
||||
if((ENV) && (ENV === 'production')){
|
||||
serviceWorkerRegistration();
|
||||
} */
|
||||
}
|
||||
|
||||
/* if(module.hot){
|
||||
/*if(module.hot){
|
||||
module.hot.accept();
|
||||
} */
|
||||
}*/
|
||||
|
@ -1,2 +1,3 @@
|
||||
const initialState = {};
|
||||
import { IInitialState } from './';
|
||||
const initialState: IInitialState = {};
|
||||
export default initialState;
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func LoadEnv() {
|
||||
err := godotenv.Load(".env")
|
||||
err := godotenv.Load("../../.env")
|
||||
if err != nil {
|
||||
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 {
|
||||
// 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
|
||||
cmd := exec.Command("node", jsFilePath, url)
|
||||
|
@ -32,6 +32,7 @@ func RegisterHandlers(e *echo.Echo, paths []string) {
|
||||
//return c.File(filePath)
|
||||
url := c.Request().URL.String()
|
||||
component := utils.JsxToString(url)
|
||||
preloadedState := utils.GetPreloadedState();
|
||||
html := `<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
@ -47,9 +48,9 @@ func RegisterHandlers(e *echo.Echo, paths []string) {
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">`+ component +`</div>
|
||||
<!-- <script>
|
||||
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
|
||||
</script> -->
|
||||
<script>
|
||||
window.__PRELOADED_STATE__ = JSON.stringify(`+ preloadedState+`).replace(/</g, '\\u003c')
|
||||
</script>
|
||||
<script src="assets/app-frontend.js" type="text/javascript"></script>
|
||||
<script src="assets/vendor-vendors.js" type="text/javascript"></script>
|
||||
</body>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import dotenv from 'dotenv'
|
||||
import { config as envConfig } from './config';
|
||||
import webpack from 'webpack';
|
||||
import CompressionWebpackPlugin from 'compression-webpack-plugin';
|
||||
@ -15,7 +16,8 @@ import { resolveTsAliases } from 'resolve-ts-aliases';
|
||||
const ROOT_DIR = path.resolve(__dirname);
|
||||
const resolvePath = (...args) => path.resolve(ROOT_DIR, ...args);
|
||||
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 nodeExternals = require('webpack-node-externals');
|
||||
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 = {
|
||||
entry: {
|
||||
frontend: `${ROOT_DIR}/src/frontend/index.tsx`,
|
||||
@ -112,6 +125,7 @@ const configReact = {
|
||||
}),
|
||||
new ESLintPlugin(),
|
||||
new webpack.EnvironmentPlugin({
|
||||
envKeys,
|
||||
...envConfig,
|
||||
}),
|
||||
new CopyPlugin({
|
||||
@ -200,9 +214,67 @@ const configTSXConversion = {
|
||||
],
|
||||
}),
|
||||
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: {
|
||||
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