diff --git a/.env.example b/.env.example index e7ac79d..f90dd93 100644 --- a/.env.example +++ b/.env.example @@ -3,4 +3,8 @@ ENV= #Default production #App Port PORT= #Default 80 #PUBLIC URL -PUBLIC_URL= #Default / \ No newline at end of file +PUBLIC_URL= #Default 'auto' +#Prefix URL +PREFIX_URL= #Default '' +#ONLY EXACT PATH +ONLY_EXACT_PATH= #Default false \ No newline at end of file diff --git a/README.md b/README.md index 8c885b0..25f00fa 100644 --- a/README.md +++ b/README.md @@ -25,13 +25,17 @@ You will need to create a new .env file at the root of the project for global co This is an exaple of config. ``` #Environment -ENV=development #Default production +ENV= #Default production #App Port -PORT=3000 #Default 80 +PORT= #Default 80 #PUBLIC URL -#PUBLIC_URL= #Default / +PUBLIC_URL= #Default 'auto' +#Prefix URL +PREFIX_URL= #Default '' +#ONLY EXACT PATH +ONLY_EXACT_PATH= #Default false ``` -The default environment is production, the app port defauld is 80 and the default public url is "/". +The default environment is production, the app port defauld is 80 and the default public url is "auto", use prefix url if you want a prefix on base url, use exact path to validate if you want to have strict exact paths. ### For Development In the terminal run: diff --git a/bin/cli.js b/bin/cli.js index 7013963..85a5a4d 100644 --- a/bin/cli.js +++ b/bin/cli.js @@ -102,7 +102,7 @@ if(!cleanGitHistory) process.exit(-1); console.log("Congratulations! You are ready. Follow the following commands to start"); console.log(`cd ${repoName}`); -console.log('Create a .env file with ENV=development(defauld: production), PORT=3000 (default: 80), PUBLIC_URL=your_public_url(optional)(default: /)'); +console.log('Create a .env file with ENV=development(defauld: production), PORT=3000 (default: 80), PUBLIC_URL=your_public_url(optional)(default: "auto"), PREFIX_URL=your_prefix_url(optional)(default: ""), ONLY_EXACT_PATH=true(optional)(default: false)'); console.log(`Then you can run: npm start:dev`); const deleteFolders = isWin ? runCommand(deleteFoldersCommandWindows) : runCommand(deleteFoldersCommand); diff --git a/config/index.ts b/config/index.ts index ed47e9e..895f8b4 100644 --- a/config/index.ts +++ b/config/index.ts @@ -1,11 +1,15 @@ -export const config = { - ENV: process.env.ENV, - PORT: process.env.PORT, - PUBLIC_URL: process.env.PUBLIC_URL, -}; - export const deFaultValues = { ENV: 'production', - PORT: 3000, - PUBLIC_URL: '/', -} \ No newline at end of file + PORT: 80, + PUBLIC_URL: 'auto', + PREFIX_URL: '', + ONLY_EXACT_PATH: false, +} + +export const config = { + ENV: process.env.ENV ? process.env.ENV : deFaultValues.ENV, + PORT: process.env.PORT ? process.env.PORT : deFaultValues.PORT, + PUBLIC_URL: process.env.PUBLIC_URL ? process.env.PUBLIC_URL : deFaultValues.PUBLIC_URL, + PREFIX_URL: process.env.PREFIX_URL ? process.env.PREFIX_URL : deFaultValues.PREFIX_URL, + ONLY_EXACT_PATH: process.env.ONLY_EXACT_PATH ? process.env.ONLY_EXACT_PATH === 'true' : deFaultValues.ONLY_EXACT_PATH, +}; diff --git a/jest.config.js b/jest.config.js index 3c2e9eb..11a3496 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,6 +3,9 @@ module.exports = { "testEnvironment": "jsdom", moduleNameMapper: { "\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/__mocks__/fileMock.ts", + "@components": "/src/frontend/components/", + "@styles": "/src/frontend/styles/", + "@config": "/config/", "\\.(css|sass|scss|less)$": "identity-obj-proxy" }, }; \ No newline at end of file diff --git a/package.json b/package.json index 22f7ef7..52b88d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aleleba/create-react-ssr", - "version": "3.5.7", + "version": "3.6.0", "description": "Starter Kit of server side render of react", "bin": "./bin/cli.js", "main": "src/server/index", diff --git a/src/frontend/logo.svg b/public/img/logo.svg similarity index 100% rename from src/frontend/logo.svg rename to public/img/logo.svg diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index e2013b5..f23b1b6 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -1,4 +1,8 @@ declare module "*.svg" { const content: any; export default content; -} \ No newline at end of file +} + +declare module "@config" { + export const config: any; +} diff --git a/src/frontend/components/InitialComponent.tsx b/src/frontend/components/InitialComponent.tsx index 83178b9..43b13d6 100644 --- a/src/frontend/components/InitialComponent.tsx +++ b/src/frontend/components/InitialComponent.tsx @@ -1,12 +1,11 @@ import React from 'react'; -import logo from '../logo.svg'; import './InitialComponent.scss'; import { Link } from "react-router-dom"; const InitialComponent = () => (
- logo + logo

Edit src/frontend/InitialComponent.jsx and save to reload.

diff --git a/src/frontend/components/OtherComponent.tsx b/src/frontend/components/OtherComponent.tsx index 7b539b9..39a7eed 100644 --- a/src/frontend/components/OtherComponent.tsx +++ b/src/frontend/components/OtherComponent.tsx @@ -6,7 +6,7 @@ import { Link } from "react-router-dom"; const OtherComponent = () => (
- logo + logo

Edit src/frontend/OtherComponent.jsx and save to reload.

diff --git a/src/frontend/index.tsx b/src/frontend/index.tsx index 51e844d..3e05c96 100644 --- a/src/frontend/index.tsx +++ b/src/frontend/index.tsx @@ -28,7 +28,7 @@ interface IHot { accept: any } -const { ENV } = config; +const { ENV, PREFIX_URL } = config; const preloadedState = window.__PRELOADED_STATE__; const store = setStore({ initialState: preloadedState }); @@ -41,7 +41,7 @@ if(ENV === 'development') { const root = createRoot(container); root.render( - + @@ -51,7 +51,7 @@ if(ENV === 'development') { // add "const root" to be able to rerender. ENV === 'production' && hydrateRoot(container, - + , diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 9fd9899..0bac04a 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -12,5 +12,4 @@ const INITIAL_COMPONENT = { element: , }; - export default [ INITIAL_COMPONENT, OTHER_COMPONENT ]; diff --git a/src/server/server.tsx b/src/server/server.tsx index 4735560..25ebdaf 100644 --- a/src/server/server.tsx +++ b/src/server/server.tsx @@ -2,7 +2,7 @@ import express from 'express'; import webpack from 'webpack'; import helmet from 'helmet'; -import { config } from '../../config'; +import { config } from '@config'; //Dependencies of HotReloading import webpackConfig from '../../webpack.config.dev'; @@ -24,9 +24,9 @@ import { getHashManifest, haveVendorsCss } from './utilsServer'; //App import App from '../frontend/components/App'; -const { ENV, PORT } = config; +const { ENV, PORT, PREFIX_URL, ONLY_EXACT_PATH } = config; -const routesUrls = routes.map( route => route.path); +const routesUrls = routes.map( route => route.path ); const app = express(); @@ -47,6 +47,7 @@ if(ENV === 'development'){ }else{ const baseUrl = __dirname.replace(/\/server(.*)/,''); const fullURL = `${baseUrl}` ; + console.log(fullURL) app .use((req, res, next) => { if(!req.hashManifest) req.hashManifest = getHashManifest(); @@ -102,17 +103,21 @@ const setResponse = (html, preloadedState, manifest) => { }; const renderApp = (req, res, next) => { - if(routesUrls.includes(req.url)){ - const store = setStore({ initialState }); - const preloadedState = store.getState(); - const html = renderToString( - // @ts-ignore:next-line - - - - - - ); + const store = setStore({ initialState }); + const preloadedState = store.getState(); + const html = renderToString( + // @ts-ignore:next-line + + + + + + ); + if(ONLY_EXACT_PATH){ + if(routesUrls.includes(req.url)){ + res.send(setResponse(html, preloadedState, req.hashManifest)); + } + } else { res.send(setResponse(html, preloadedState, req.hashManifest)); } next(); @@ -121,7 +126,6 @@ const renderApp = (req, res, next) => { app .get('*', renderApp); - app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); diff --git a/src/server/utilsServer.ts b/src/server/utilsServer.ts index 9b7c656..5d9f692 100644 --- a/src/server/utilsServer.ts +++ b/src/server/utilsServer.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import { config } from '../../config'; -const { ENV } = config; +const { ENV } = config export const getHashManifest = () => { try { @@ -17,7 +17,7 @@ export const getHashManifest = () => { export const haveVendorsCss = (manifest, memoryFs) => { try { const baseUrl = __dirname.replace(/\/server(.*)/,''); - const fullURL = `${baseUrl}${manifest ? manifest['vendors.css'] : '/build/assets/vendors.css'}`; + const fullURL = `${baseUrl}${manifest ? `/${manifest['vendors.css']}` : '/build/assets/vendors.css'}`; ENV === 'production' && fs.readFileSync(fullURL).toString(); ENV === 'development' && memoryFs.readFileSync(fullURL).toString(); return true @@ -25,4 +25,4 @@ export const haveVendorsCss = (manifest, memoryFs) => { // console.error(err); return false } -}; +}; \ No newline at end of file diff --git a/webpack.config.dev.server.ts b/webpack.config.dev.server.ts index cebc25a..9a5c7d5 100644 --- a/webpack.config.dev.server.ts +++ b/webpack.config.dev.server.ts @@ -21,10 +21,10 @@ const config: Configuration = { resolve: { extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'], alias: { - '@app': path.resolve(__dirname, 'src/frontend/components/App'), '@components': path.resolve(__dirname, 'src/frontend/components/'), '@styles': path.resolve(__dirname, 'src/frontend/styles/'), - } + '@config': path.resolve(__dirname, 'config/'), + }, }, module: { rules: [ @@ -58,7 +58,7 @@ const config: Configuration = { test: fontsAndImagesExtensions, loader: 'file-loader', options: { - name: '/assets/[name].[ext]', + name: 'assets/[name].[ext]', emitFile: false, }, }, diff --git a/webpack.config.dev.ts b/webpack.config.dev.ts index b555a70..626a089 100644 --- a/webpack.config.dev.ts +++ b/webpack.config.dev.ts @@ -1,6 +1,6 @@ import path from 'path'; import fs from 'fs'; -import { deFaultValues } from './config'; +import { config as envConfig } from './config'; import webpack, { Configuration } from 'webpack'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'; @@ -33,18 +33,19 @@ if(fs.existsSync(`${ROOT_DIR}/../public/img`)){ } const config: Configuration = { - entry: ['webpack-hot-middleware/client?path=/reload_wss&timeout=2000&reload=true&autoConnect=true', `${ROOT_DIR}/../src/frontend/index.tsx`], + entry: [`webpack-hot-middleware/client?path=${envConfig.PREFIX_URL}/reload_wss&timeout=2000&reload=true&autoConnect=true`, `${ROOT_DIR}/../src/frontend/index.tsx`], output: { path: BUILD_DIR, filename: 'assets/app.js', - publicPath: deFaultValues.PUBLIC_URL, + publicPath: envConfig.PUBLIC_URL, }, resolve: { extensions: ['.js', '.jsx','.ts','.tsx', '.json'], alias: { - '@components': path.resolve(__dirname, 'src/frontend/components/'), - '@styles': path.resolve(__dirname, 'src/frontend/styles/'), - } + '@components': path.resolve(__dirname, '../src/frontend/components/'), + '@styles': path.resolve(__dirname, '../src/frontend/styles/'), + '@config': path.resolve(__dirname, '../config/'), + }, }, devtool: 'inline-source-map', mode: 'development', @@ -92,7 +93,7 @@ const config: Configuration = { }), new ESLintPlugin(), new webpack.EnvironmentPlugin({ - ...deFaultValues, + ...envConfig, }), new CopyPlugin({ patterns: copyPatterns diff --git a/webpack.config.ts b/webpack.config.ts index 553895d..5b17b3b 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -1,6 +1,6 @@ import path from 'path'; import fs from 'fs'; -import { deFaultValues } from './config'; +import { config as envConfig } from './config'; import webpack from 'webpack'; import CompressionWebpackPlugin from 'compression-webpack-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; @@ -46,13 +46,14 @@ const frontendConfig = { output: { path: BUILD_DIR, filename: 'assets/app-[name]-[fullhash].js', - publicPath: deFaultValues.PUBLIC_URL, + publicPath: envConfig.PUBLIC_URL, }, resolve: { extensions: ['.js', '.jsx','.ts','.tsx', '.json'], alias: { '@components': path.resolve(__dirname, 'src/frontend/components/'), '@styles': path.resolve(__dirname, 'src/frontend/styles/'), + '@config': path.resolve(__dirname, 'config/'), } }, mode: 'production', @@ -100,6 +101,7 @@ const frontendConfig = { }), new WebpackManifestPlugin({ fileName: 'assets/manifest-hash.json', + publicPath: envConfig.PREFIX_URL, }), new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: [ @@ -109,7 +111,7 @@ const frontendConfig = { }), new ESLintPlugin(), new webpack.EnvironmentPlugin({ - ...deFaultValues, + ...envConfig, }), new CopyPlugin({ patterns: copyPatterns @@ -154,13 +156,14 @@ const serverConfig = { output: { path: path.resolve(__dirname, 'build'), filename: 'server/app-[name].js', - publicPath: deFaultValues.PUBLIC_URL, + publicPath: envConfig.PUBLIC_URL, }, resolve: { extensions: ['.js', '.jsx','.ts','.tsx', '.json'], alias: { '@components': path.resolve(__dirname, 'src/frontend/components/'), '@styles': path.resolve(__dirname, 'src/frontend/styles/'), + '@config': path.resolve(__dirname, 'config/'), } }, mode: 'production', @@ -208,12 +211,10 @@ const serverConfig = { }), new WebpackManifestPlugin({ fileName: 'assets/manifest-hash.json', + publicPath: envConfig.PREFIX_URL, }), new CleanWebpackPlugin(), new ESLintPlugin(), - new webpack.EnvironmentPlugin({ - ...deFaultValues, - }), new InjectManifest({ swSrc: './service-worker.ts', swDest: 'service-worker.js',