From 30e3ee4570bfc9ca0f730f33dd3b1f27cfa58218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Saltvik?= Date: Mon, 20 Dec 2021 18:44:46 +0100 Subject: [PATCH] feat: add serverless example --- examples/basic-serverless/README.md | 19 ++++++ examples/basic-serverless/gitignore | 10 ++++ examples/basic-serverless/package.json | 25 ++++++++ examples/basic-serverless/public/favicon.ico | Bin 0 -> 32988 bytes examples/basic-serverless/public/robots.txt | 2 + examples/basic-serverless/razzle.config.js | 7 +++ examples/basic-serverless/sandbox.config.json | 5 ++ examples/basic-serverless/src/App.css | 13 +++++ examples/basic-serverless/src/App.js | 6 ++ examples/basic-serverless/src/App.test.js | 10 ++++ examples/basic-serverless/src/client.js | 9 +++ examples/basic-serverless/src/index.js | 27 +++++++++ examples/basic-serverless/src/server.js | 55 ++++++++++++++++++ 13 files changed, 188 insertions(+) create mode 100644 examples/basic-serverless/README.md create mode 100644 examples/basic-serverless/gitignore create mode 100644 examples/basic-serverless/package.json create mode 100644 examples/basic-serverless/public/favicon.ico create mode 100644 examples/basic-serverless/public/robots.txt create mode 100644 examples/basic-serverless/razzle.config.js create mode 100644 examples/basic-serverless/sandbox.config.json create mode 100644 examples/basic-serverless/src/App.css create mode 100644 examples/basic-serverless/src/App.js create mode 100644 examples/basic-serverless/src/App.test.js create mode 100644 examples/basic-serverless/src/client.js create mode 100644 examples/basic-serverless/src/index.js create mode 100644 examples/basic-serverless/src/server.js diff --git a/examples/basic-serverless/README.md b/examples/basic-serverless/README.md new file mode 100644 index 000000000..b8c750af2 --- /dev/null +++ b/examples/basic-serverless/README.md @@ -0,0 +1,19 @@ +# Razzle Basic Example + +## How to use + + +Create and start the example: + +```bash +npx create-razzle-app --example basic basic + +cd basic +yarn start +``` + + + +## Idea behind the example +This is a basic, bare-bones example of how to use razzle. It satisfies the entry points +`src/index.js` for the server and and `src/client.js` for the browser. diff --git a/examples/basic-serverless/gitignore b/examples/basic-serverless/gitignore new file mode 100644 index 000000000..13d409785 --- /dev/null +++ b/examples/basic-serverless/gitignore @@ -0,0 +1,10 @@ +logs +*.log +npm-debug.log* +.DS_Store + +coverage +node_modules +build +public/static +.env.*.local \ No newline at end of file diff --git a/examples/basic-serverless/package.json b/examples/basic-serverless/package.json new file mode 100644 index 000000000..f940795de --- /dev/null +++ b/examples/basic-serverless/package.json @@ -0,0 +1,25 @@ +{ + "name": "razzle-examples-basic-serverless", + "version": "4.2.12", + "license": "MIT", + "scripts": { + "start": "razzle start", + "build": "razzle build", + "test": "razzle test --env=jsdom", + "start:prod": "NODE_ENV=production node build/server.js" + }, + "dependencies": { + "express": "^4.17.1", + "react": "^17.0.1", + "react-dom": "^17.0.1" + }, + "devDependencies": { + "razzle": "4.2.12", + "razzle-dev-utils": "4.2.12", + "mini-css-extract-plugin": "^0.9.0", + "html-webpack-plugin": "^4.5.2", + "webpack": "^4.44.1", + "babel-preset-razzle": "4.2.12", + "webpack-dev-server": "^3.11.2" + } +} diff --git a/examples/basic-serverless/public/favicon.ico b/examples/basic-serverless/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..46bceb8c71a1c5a55b97fb1a7ddb7eeebd2ab277 GIT binary patch literal 32988 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
Welcome to Razzle.
; + +export default App; diff --git a/examples/basic-serverless/src/App.test.js b/examples/basic-serverless/src/App.test.js new file mode 100644 index 000000000..17cf4a181 --- /dev/null +++ b/examples/basic-serverless/src/App.test.js @@ -0,0 +1,10 @@ +import App from './App'; +import React from 'react'; +import ReactDOM from 'react-dom'; + +describe('', () => { + test('renders without exploding', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + }); +}); diff --git a/examples/basic-serverless/src/client.js b/examples/basic-serverless/src/client.js new file mode 100644 index 000000000..fbbe57121 --- /dev/null +++ b/examples/basic-serverless/src/client.js @@ -0,0 +1,9 @@ +import React from 'react'; +import { hydrate } from 'react-dom'; +import App from './App'; + +hydrate(, document.getElementById('root')); + +if (module.hot) { + module.hot.accept(); +} diff --git a/examples/basic-serverless/src/index.js b/examples/basic-serverless/src/index.js new file mode 100644 index 000000000..dd93c3284 --- /dev/null +++ b/examples/basic-serverless/src/index.js @@ -0,0 +1,27 @@ +import express from 'express'; + +let app = require('./server').default; + +if (module.hot) { + module.hot.accept('./server', function() { + console.log('🔁 HMR Reloading `./server`...'); + try { + app = require('./server').default; + } catch (error) { + console.error(error); + } + }); + console.info('✅ Server-side HMR Enabled!'); +} + +const port = process.env.PORT || 3000; + +export default express() + .use((req, res) => app.handle(req, res)) + .listen(port, function(err) { + if (err) { + console.error(err); + return; + } + console.log(`> Started on port ${port}`); + }); diff --git a/examples/basic-serverless/src/server.js b/examples/basic-serverless/src/server.js new file mode 100644 index 000000000..763d54994 --- /dev/null +++ b/examples/basic-serverless/src/server.js @@ -0,0 +1,55 @@ +import App from './App'; +import React from 'react'; +import express from 'express'; +import { renderToString } from 'react-dom/server'; + +const assets = require(process.env.RAZZLE_ASSETS_MANIFEST); + +const cssLinksFromAssets = (assets, entrypoint) => { + return assets[entrypoint] ? assets[entrypoint].css ? + assets[entrypoint].css.map(asset=> + `` + ).join('') : '' : ''; +}; + +const jsScriptTagsFromAssets = (assets, entrypoint, extra = '') => { + return assets[entrypoint] ? assets[entrypoint].js ? + assets[entrypoint].js.map(asset=> + `` + ).join('') : '' : ''; +}; + +export const renderApp = (req, res) => { + const markup = renderToString(); + + const html = + // prettier-ignore + ` + + + + + Welcome to Razzle + + ${cssLinksFromAssets(assets, 'client')} + + +
${markup}
+ ${jsScriptTagsFromAssets(assets, 'client', ' defer crossorigin')} + +`; + + return { html }; +}; + +const server = express(); + +server + .disable('x-powered-by') + .use(express.static(process.env.RAZZLE_PUBLIC_DIR)) + .get('/*', (req, res) => { + const { html } = renderApp(req, res); + res.send(html); + }); + +export default server;