diff --git a/config/run.js b/config/run.js index adf9b0d..2eeb2fc 100644 --- a/config/run.js +++ b/config/run.js @@ -85,4 +85,3 @@ app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`)); -debugger; \ No newline at end of file diff --git a/config/webpack.config.js b/config/webpack.config.js index 4eadf9f..c0ecce5 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -27,6 +27,9 @@ const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpack const typescriptFormatter = require('react-dev-utils/typescriptFormatter'); const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); const LoadablePlugin = require('@loadable/webpack-plugin') +var HtmlWebpackSkipAssetsPlugin = require('html-webpack-skip-assets-plugin').HtmlWebpackSkipAssetsPlugin; +const PreloadWebpackPlugin = require("preload-webpack-plugin"); + const postcssNormalize = require('postcss-normalize'); @@ -81,6 +84,8 @@ module.exports = function (webpackEnv) { const isEnvDevelopment = webpackEnv === 'development'; const isEnvProduction = webpackEnv === 'production' ; const isEnvServer = webpackEnv === 'server'; + const isServerDEV =process.env.SERVER_DEV === "true" + console.info(isServerDEV) const isProdOrServer = isEnvProduction || isEnvServer const iDevOrServer = isEnvDevelopment || isEnvServer @@ -179,7 +184,7 @@ module.exports = function (webpackEnv) { ].filter(Boolean) const config = Object.assign({ - mode: isEnvProduction ? 'production' : isEnvDevelopment ? 'development' : isEnvServer && 'none', + mode: isEnvProduction ? 'production' : (isEnvDevelopment || isServerDEV) ? 'development' : isEnvServer && 'none', // Stop compilation early in production bail: isProdOrServer, devtool: isProdOrServer @@ -190,7 +195,7 @@ module.exports = function (webpackEnv) { // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. entry: - isEnvDevelopment && !shouldUseReactRefresh + (isEnvDevelopment ) && !shouldUseReactRefresh ? [ // Include an alternative client for WebpackDevServer. A client's job is to // connect to WebpackDevServer by a socket and get notified about changes. @@ -218,7 +223,7 @@ module.exports = function (webpackEnv) { :paths.appIndexJs, output:Object.assign({ // The build folder. - path: isEnvProduction ? paths.appBuild : isEnvServer ? paths.appServerBuild : undefined, + path: isEnvServer ? paths.appServerBuild : paths.appBuild, // Add /* filename */ comments to generated require()s in the output. pathinfo: isEnvDevelopment, // There will be one main bundle, and one file per asynchronous chunk. @@ -575,7 +580,7 @@ module.exports = function (webpackEnv) { }, isEnvProduction ? { - minify: { + minify: false /* { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, @@ -586,11 +591,31 @@ module.exports = function (webpackEnv) { minifyJS: true, minifyCSS: true, minifyURLs: true, - }, + }, */ } : undefined ) ), + + new HtmlWebpackPlugin( + Object.assign( + {}, + { + inject: true, + template: paths.appHtml, + filename: "index-ssr.html", + minify:false, + excludeAssets: [/\.js$/i] + } + ) + ), + new HtmlWebpackSkipAssetsPlugin(), + + // new PreloadWebpackPlugin({ + // rel: "preload", + // include: "initial" + // }), + // Inlines the webpack runtime script. This script is too small to warrant // a network request. // https://github.com/facebook/create-react-app/issues/5358 @@ -613,10 +638,10 @@ module.exports = function (webpackEnv) { // Otherwise React will be compiled in the very slow development mode. new webpack.DefinePlugin(env.stringified), // This is necessary to emit hot updates (CSS and Fast Refresh): - isEnvDevelopment && new webpack.HotModuleReplacementPlugin(), + (isEnvDevelopment ) && new webpack.HotModuleReplacementPlugin(), // Experimental hot reloading for React . // https://github.com/facebook/react/tree/master/packages/react-refresh - isEnvDevelopment && + (isEnvDevelopment ) && shouldUseReactRefresh && new ReactRefreshWebpackPlugin({ overlay: { @@ -632,12 +657,12 @@ module.exports = function (webpackEnv) { // Watcher doesn't work well if you mistype casing in a path so we use // a plugin that prints an error when you attempt to do this. // See https://github.com/facebook/create-react-app/issues/240 - isEnvDevelopment && new CaseSensitivePathsPlugin(), + (isEnvDevelopment || isServerDEV) && new CaseSensitivePathsPlugin(), // If you require a missing module and then `npm install` it, you still have // to restart the development server for webpack to discover it. This plugin // makes the discovery automatic so you don't have to restart. // See https://github.com/facebook/create-react-app/issues/186 - isEnvDevelopment && + (isEnvDevelopment || isServerDEV) && new WatchMissingNodeModulesPlugin(paths.appNodeModules), isEnvProduction && new MiniCssExtractPlugin({ diff --git a/config/webpackDevServer.config.js b/config/webpackDevServer.config.js index 6c43a8a..fbbdae7 100644 --- a/config/webpackDevServer.config.js +++ b/config/webpackDevServer.config.js @@ -1,4 +1,4 @@ -'use strict'; + const fs = require('fs'); const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware'); @@ -15,7 +15,7 @@ const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node' const sockPort = process.env.WDS_SOCKET_PORT; module.exports = function (proxy, allowedHost) { - return { + return Object.assign( { // WebpackDevServer 2.4.3 introduced a security fix that prevents remote // websites from potentially accessing local content through DNS rebinding: // https://github.com/webpack/webpack-dev-server/issues/887 @@ -126,5 +126,5 @@ module.exports = function (proxy, allowedHost) { // https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432 app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath)); }, - }; + },{hot: false, liveReload: true, writeToDisk: true, open : false /* process.env.NODE_ENV === "server" ? false : true */ }); }; diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 0000000..8089c97 --- /dev/null +++ b/nodemon.json @@ -0,0 +1,4 @@ +{ + "watch": ["config/run.js", "build/"], + "ext": "js, css, html" + } \ No newline at end of file diff --git a/package.json b/package.json index d25520b..8aad918 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,9 @@ "workbox-webpack-plugin": "5.1.4" }, "scripts": { - "start": "node scripts/start.js", + "start": "PORT=3002 WDS_SOCKET_PORT=3002 node scripts/start.js", + "startServer": "PORT=3001 SERVER_DEV=true node scripts/startServer.js", + "double": " npm run start & npm run startServer & wait", "build": "node scripts/build.js", "builds": "node scripts/buildserver.js", "buildse": "env-cmd -f ./server/.env.serverwatch node scripts/buildserver.js", @@ -92,6 +94,7 @@ "d": "node --inspect-brk scripts/build.js", "ds": "node --inspect-brk scripts/buildserver.js", "dr": "node config/run.js", + "watch:express": "nodemon config/run.js", "postinstall": "patch-package" }, "eslintConfig": { @@ -182,6 +185,8 @@ "@types/react-router-dom": "^5.1.7", "copyfiles": "^2.4.1", "env-cmd": "^10.1.0", - "patch-package": "^6.2.2" + "html-webpack-skip-assets-plugin": "^1.0.1", + "patch-package": "^6.2.2", + "preload-webpack-plugin": "^2.3.0" } } diff --git a/scripts/start.js b/scripts/start.js index 92c2671..aaa1993 100644 --- a/scripts/start.js +++ b/scripts/start.js @@ -1,4 +1,4 @@ -'use strict'; + // Do this as the first thing so that any code reading it knows the right env. process.env.BABEL_ENV = 'development'; @@ -140,7 +140,8 @@ checkBrowsers(paths.appPath, isInteractive) } console.log(chalk.cyan('Starting the development server...\n')); - openBrowser(urls.localUrlForBrowser); + if (serverConfig.open) openBrowser(urls.localUrlForBrowser); + }); ['SIGINT', 'SIGTERM'].forEach(function (sig) { diff --git a/scripts/startServer.js b/scripts/startServer.js new file mode 100644 index 0000000..102a010 --- /dev/null +++ b/scripts/startServer.js @@ -0,0 +1,166 @@ + + +// Do this as the first thing so that any code reading it knows the right env. +process.env.BABEL_ENV = 'server'; +process.env.NODE_ENV = 'server'; + +// Makes the script crash on unhandled rejections instead of silently +// ignoring them. In the future, promise rejections that are not handled will +// terminate the Node.js process with a non-zero exit code. +process.on('unhandledRejection', err => { + throw err; +}); + +// Ensure environment variables are read. +require('../config/env'); + + +const fs = require('fs'); +const chalk = require('react-dev-utils/chalk'); +const webpack = require('webpack'); +const WebpackDevServer = require('webpack-dev-server'); +const clearConsole = require('react-dev-utils/clearConsole'); +const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); +const { + choosePort, + createCompiler, + prepareProxy, + prepareUrls, +} = require('react-dev-utils/WebpackDevServerUtils'); +const openBrowser = require('react-dev-utils/openBrowser'); +const semver = require('semver'); +const paths = require('../config/paths'); +const configFactory = require('../config/webpack.config'); +const createDevServerConfig = require('../config/webpackDevServer.config'); +const getClientEnvironment = require('../config/env'); +const react = require(require.resolve('react', { paths: [paths.appPath] })); + +const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)); +const useYarn = fs.existsSync(paths.yarnLockFile); +const isInteractive = process.stdout.isTTY; + +// Warn and crash if required files are missing +if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { + process.exit(1); +} + +// Tools like Cloud9 rely on this. +const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; +const HOST = process.env.HOST || '0.0.0.0'; + +if (process.env.HOST) { + console.log( + chalk.cyan( + `Attempting to bind to HOST environment variable: ${chalk.yellow( + chalk.bold(process.env.HOST) + )}` + ) + ); + console.log( + `If this was unintentional, check that you haven't mistakenly set it in your shell.` + ); + console.log( + `Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}` + ); + console.log(); +} + +// We require that you explicitly set browsers and do not fall back to +// browserslist defaults. +const { checkBrowsers } = require('react-dev-utils/browsersHelper'); +checkBrowsers(paths.appPath, isInteractive) + .then(() => { + // We attempt to use the default port but if it is busy, we offer the user to + // run on a different port. `choosePort()` Promise resolves to the next free port. + return choosePort(HOST, DEFAULT_PORT); + }) + .then(port => { + if (port == null) { + // We have not found a port. + return; + } + + const config = configFactory('server'); + const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; + const appName = require(paths.appPackageJson).name; + + const useTypeScript = fs.existsSync(paths.appTsConfig); + const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true'; + const urls = prepareUrls( + protocol, + HOST, + port, + paths.publicUrlOrPath.slice(0, -1) + ); + const devSocket = { + warnings: warnings => + devServer.sockWrite(devServer.sockets, 'warnings', warnings), + errors: errors => + devServer.sockWrite(devServer.sockets, 'errors', errors), + }; + // Create a webpack compiler that is configured with custom messages. + const compiler = createCompiler({ + appName, + config, + devSocket, + urls, + useYarn, + useTypeScript, + tscCompileOnError, + webpack, + }); + // Load proxy config + const proxySetting = require(paths.appPackageJson).proxy; + const proxyConfig = prepareProxy( + proxySetting, + paths.appPublic, + paths.publicUrlOrPath + ); + // Serve webpack assets generated by the compiler over a web server. + const serverConfig = createDevServerConfig( + proxyConfig, + urls.lanUrlForConfig + ); + const devServer = new WebpackDevServer(compiler, serverConfig); + // Launch WebpackDevServer. + devServer.listen(port, HOST, err => { + if (err) { + return console.log(err); + } + if (isInteractive) { + clearConsole(); + } + + if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) { + console.log( + chalk.yellow( + `Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.` + ) + ); + } + + console.log(chalk.cyan('Starting the server server...\n')); + if (serverConfig.open) openBrowser(urls.localUrlForBrowser); + }); + + ['SIGINT', 'SIGTERM'].forEach(function (sig) { + process.on(sig, function () { + devServer.close(); + process.exit(); + }); + }); + + if (process.env.CI !== 'true') { + // Gracefully exit when stdin ends + process.stdin.on('end', function () { + devServer.close(); + process.exit(); + }); + } + }) + .catch(err => { + if (err && err.message) { + console.log(err.message); + } + process.exit(1); + }); diff --git a/src/App.tsx b/src/App.tsx index 781c617..740deab 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -39,12 +39,17 @@ const Category = () => ( ); +function createMarkup() { + return {__html: '
Edit src/App.tsx and save to reload.