diff --git a/client/package-lock.json b/client/package-lock.json index f35a7ac..73785ae 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -59,6 +59,7 @@ "eslint": "^8.13.0", "eslint-plugin-react": "^7.29.4", "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", "mini-css-extract-plugin": "^2.6.0", "react-refresh": "^0.12.0", "redux-devtools-extension": "^2.13.9", @@ -67,6 +68,7 @@ "style-loader": "^3.3.1", "terser-webpack-plugin": "^5.3.1", "typescript": "^4.6.3", + "url-loader": "^4.1.1", "webpack-cli": "^4.9.2", "webpack-dev-middleware": "^5.3.1", "webpack-dev-server": "^4.8.1", @@ -5470,6 +5472,44 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/filelist": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.3.tgz", @@ -9760,6 +9800,51 @@ "punycode": "^2.1.0" } }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/use-sync-external-store": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.0.0.tgz", @@ -14530,6 +14615,29 @@ "flat-cache": "^3.0.4" } }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, "filelist": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.3.tgz", @@ -17596,6 +17704,30 @@ "punycode": "^2.1.0" } }, + "url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, "use-sync-external-store": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.0.0.tgz", diff --git a/client/package.json b/client/package.json index b4e3096..e746494 100644 --- a/client/package.json +++ b/client/package.json @@ -76,6 +76,7 @@ "eslint": "^8.13.0", "eslint-plugin-react": "^7.29.4", "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", "mini-css-extract-plugin": "^2.6.0", "react-refresh": "^0.12.0", "redux-devtools-extension": "^2.13.9", @@ -84,6 +85,7 @@ "style-loader": "^3.3.1", "terser-webpack-plugin": "^5.3.1", "typescript": "^4.6.3", + "url-loader": "^4.1.1", "webpack-cli": "^4.9.2", "webpack-dev-middleware": "^5.3.1", "webpack-dev-server": "^4.8.1", diff --git a/client/public/favicon.ico b/client/public/favicon.ico new file mode 100644 index 0000000..a11777c Binary files /dev/null and b/client/public/favicon.ico differ diff --git a/client/public/logo192.png b/client/public/logo192.png new file mode 100644 index 0000000..fc44b0a Binary files /dev/null and b/client/public/logo192.png differ diff --git a/client/public/logo512.png b/client/public/logo512.png new file mode 100644 index 0000000..a4e47a6 Binary files /dev/null and b/client/public/logo512.png differ diff --git a/client/public/manifest.json b/client/public/manifest.json index 4cda738..8508431 100644 --- a/client/public/manifest.json +++ b/client/public/manifest.json @@ -1,8 +1,25 @@ { - "short_name": "App", - "name": "App", + "short_name": "React App", + "name": "React App SSR Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], "start_url": "/", "display": "standalone", - "theme_color": "#ffffff", + "theme_color": "#000000", "background_color": "#ffffff" } \ No newline at end of file diff --git a/client/src/frontend/components/InitialComponent.jsx b/client/src/frontend/components/InitialComponent.jsx index 8fa2a49..7340eb8 100644 --- a/client/src/frontend/components/InitialComponent.jsx +++ b/client/src/frontend/components/InitialComponent.jsx @@ -1,5 +1,26 @@ import React from 'react'; +import logo from '../logo.svg'; +import './InitialComponent.sass'; +import { Link } from "react-router-dom"; -const InitialComponent = () =>

Hello React!

; +const InitialComponent = () => ( +
+
+ logo +

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

+ + Learn React + + Other Component +
+
+); export default InitialComponent; diff --git a/client/src/frontend/components/InitialComponent.sass b/client/src/frontend/components/InitialComponent.sass new file mode 100644 index 0000000..ae0c60d --- /dev/null +++ b/client/src/frontend/components/InitialComponent.sass @@ -0,0 +1,29 @@ +.App + text-align: center + +.App-logo + height: 40vmin + pointer-events: none + +@media (prefers-reduced-motion: no-preference) + .App-logo + animation: App-logo-spin infinite 20s linear + +.App-header + min-height: 100vh + display: flex + flex-direction: column + align-items: center + justify-content: center + font-size: calc(10px + 2vmin) + color: white + +.App-link + color: #61dafb + +@keyframes App-logo-spin + from + transform: rotate(0deg) + + to + transform: rotate(360deg) \ No newline at end of file diff --git a/client/src/frontend/components/OtherComponent.jsx b/client/src/frontend/components/OtherComponent.jsx index f7b2eb8..5aa98f6 100644 --- a/client/src/frontend/components/OtherComponent.jsx +++ b/client/src/frontend/components/OtherComponent.jsx @@ -1,5 +1,18 @@ import React from 'react'; +import logo from '../logo.svg'; +import './InitialComponent.sass'; +import { Link } from "react-router-dom"; -const OtherComponent = () =>

Other Component!

; +const OtherComponent = () => ( +
+
+ logo +

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

+ Initial Component +
+
+); export default OtherComponent; diff --git a/client/src/frontend/logo.svg b/client/src/frontend/logo.svg new file mode 100644 index 0000000..9dfc1c0 --- /dev/null +++ b/client/src/frontend/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/frontend/styles/global.sass b/client/src/frontend/styles/global.sass index 92a12e5..d5631be 100644 --- a/client/src/frontend/styles/global.sass +++ b/client/src/frontend/styles/global.sass @@ -1,6 +1,4 @@ -$base-color: #c6538c -$color: rgba(black, 0.88) +$base-color: #282c34 body background-color: $base-color - color: $color diff --git a/client/src/server/index.js b/client/src/server/index.js index c977062..3f1ffc9 100644 --- a/client/src/server/index.js +++ b/client/src/server/index.js @@ -20,17 +20,26 @@ require('asset-require-hook')({ 'png', 'svg', 'gif', + 'ico', // videos 'mp4', 'avi', + // files + 'pdf', + ], + name: '/assets/media/[name].[ext]', +}); + +require('asset-require-hook')({ + extensions: [ // typography 'ttf', 'otf', 'eot', - // files - 'pdf' + 'woff', + 'woff2', ], - name: '/assets/[hash].[ext]', + name: '/assets/fonts/[name].[ext]', }); require('./server'); diff --git a/client/src/server/server.js b/client/src/server/server.js index 5f57132..70a6afc 100644 --- a/client/src/server/server.js +++ b/client/src/server/server.js @@ -76,7 +76,7 @@ const setResponse = (html, preloadedState, manifest) => { - + diff --git a/client/webpack.config.dev.js b/client/webpack.config.dev.js index eb422fa..9742fb7 100644 --- a/client/webpack.config.dev.js +++ b/client/webpack.config.dev.js @@ -4,6 +4,7 @@ const webpack = require('webpack'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); const ESLintPlugin = require('eslint-webpack-plugin'); +const CopyPlugin = require('copy-webpack-plugin'); const PUBLIC_URL = process.env.PUBLIC_URL || '/'; module.exports = { @@ -41,6 +42,21 @@ module.exports = { 'sass-loader', ], }, + { + test: /\.(png|jpg|jpeg|gif|svg|ico|mp4|avi|ttf|otf|eot|woff|woff2|pdf)$/, + loader: 'file-loader', + options: { + name: 'assets/media/[name].[ext]', + }, + }, + { + test: /\.(ttf|otf|eot|woff|woff2)$/, + loader: 'url-loader', + options: { + name: 'assets/fonts/[name].[ext]', + esModule: false, + }, + }, ] }, plugins: [ @@ -54,6 +70,22 @@ module.exports = { 'process.env': JSON.stringify(dotenv.parsed), 'process.env.PUBLIC_URL': JSON.stringify(PUBLIC_URL), }), + new CopyPlugin({ + patterns: [ + { + from: './public/manifest.json', to: '', + }, + { + from: './public/favicon.ico', to: '', + }, + { + from: './public/logo192.png', to: '', + }, + { + from: './public/logo512.png', to: '', + }, + ] + }), ], optimization: { splitChunks: { diff --git a/client/webpack.config.js b/client/webpack.config.js index fb0dbfd..601d550 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -47,6 +47,21 @@ const frontendConfig = { 'sass-loader', ], }, + { + test: /\.(png|jpg|jpeg|gif|svg|ico|mp4|avi|ttf|otf|eot|woff|woff2|pdf)$/, + loader: 'file-loader', + options: { + name: 'assets/media/[name].[ext]', + }, + }, + { + test: /\.(ttf|otf|eot|woff|woff2)$/, + loader: 'url-loader', + options: { + name: 'assets/fonts/[name].[ext]', + esModule: false, + }, + }, ], }, plugins: [ @@ -75,7 +90,16 @@ const frontendConfig = { patterns: [ { from: './public/manifest.json', to: '', - } + }, + { + from: './public/favicon.ico', to: '', + }, + { + from: './public/logo192.png', to: '', + }, + { + from: './public/logo512.png', to: '', + }, ] }), new InjectManifest({ @@ -145,6 +169,21 @@ const serverConfig = { 'sass-loader', ], }, + { + test: /\.(png|jpg|jpeg|gif|svg|ico|mp4|avi|ttf|otf|eot|woff|woff2|pdf)$/, + loader: 'file-loader', + options: { + name: 'assets/media/[name].[ext]', + }, + }, + { + test: /\.(ttf|otf|eot|woff|woff2)$/, + loader: 'url-loader', + options: { + name: 'assets/fonts/[name].[ext]', + esModule: false, + }, + }, ], }, plugins: [ @@ -164,13 +203,6 @@ const serverConfig = { 'process.env': JSON.stringify(dotenv.parsed), 'process.env.PUBLIC_URL': JSON.stringify(PUBLIC_URL), }), - new CopyPlugin({ - patterns: [ - { - from: './public/manifest.json', to: '', - } - ] - }), new InjectManifest({ swSrc: './service-worker.js', swDest: 'service-worker.js',