Last active
December 6, 2019 16:03
-
-
Save lmbuffetti/ce4484ebf0e5bbe07255df260df5bbaf to your computer and use it in GitHub Desktop.
Server Side Rendering
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bodyParser from 'body-parser'; | |
import compression from 'compression'; | |
import express from 'express'; | |
import cookieParser from 'cookie-parser'; | |
import Loadable from 'react-loadable'; | |
import render from './render'; | |
var request = require('request'); | |
var fs = require("fs"); | |
const app = express(); | |
const PORT = 46130; | |
app.use(compression()); | |
app.use(bodyParser.json()); | |
app.use(bodyParser.urlencoded({ extended: false })); | |
app.use(cookieParser()); | |
// app.use(bodyParser.json({ type: 'application/vnd.api+json' })); | |
// bloqueia o acesso ao arquivo de servidor | |
// app.use('/server.js', (req, res) => { | |
// res.status(404).send(); | |
// }); | |
// bloqueia o static renderizar o index.html antes do ssr | |
app.get('/', render); | |
// arquivos estaticos | |
app.use(express.static(`${__dirname}/`)); | |
// renderiza react para todas outras urls | |
app.use(render); | |
Loadable.preloadAll().then(() => { | |
app.listen(PORT, console.log(`App listening on port ${PORT}!`)); | |
const targetEntry = `http://localhost:${PORT}/`; | |
console.log(targetEntry); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'fs'; | |
import path from 'path'; | |
import React from 'react'; | |
import { renderToString } from 'react-dom/server'; | |
import { StaticRouter } from 'react-router-dom'; | |
import Helmet from 'react-helmet'; | |
import Loadable from 'react-loadable'; | |
import { Provider } from 'react-redux'; | |
import { Frontload, frontloadServerRender } from 'react-frontload'; | |
import { configureStore } from '../src/store'; | |
import App from '../src/App'; | |
import manifest from '../dist/manifest.json'; | |
import { getEnviroment, getConfig } from '../src/controllers/checkEnvironment'; | |
import { config } from '../src/constants/global'; | |
const analitycs = (env) => { | |
const gaId = config()[getConfig(env)].googleAnalyticsId; | |
return ` | |
<script> | |
(function(i, s, o, g, r, a, m) { | |
i['GoogleAnalyticsObject'] = r; | |
i[r] = i[r] || function() { | |
(i[r].q = i[r].q || []).push(arguments) | |
}, i[r].l = 1 * new Date(); | |
a = s.createElement(o), | |
m = s.getElementsByTagName(o)[0]; | |
a.async = 1; | |
a.src = g; | |
m.parentNode.insertBefore(a, m) | |
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); | |
ga('create', '${gaId}', 'auto'); | |
ga('set', 'anonymizeIp', true); | |
ga('send', 'pageview'); | |
</script>`; | |
}; | |
const fbPixel = (env) => { | |
const fbPixelID = config()[getConfig(env)].facebookPixel; | |
return ` | |
<script> | |
! function(f, b, e, v, n, t, s) { | |
if (f.fbq) return; | |
n = f.fbq = function() { | |
n.callMethod ? | |
n.callMethod.apply(n, arguments) : n.queue.push(arguments) | |
}; | |
if (!f._fbq) f._fbq = n; | |
n.push = n; | |
n.loaded = !0; | |
n.version = '2.0'; | |
n.queue = []; | |
t = b.createElement(e); | |
t.async = !0; | |
t.src = v; | |
s = b.getElementsByTagName(e)[0]; | |
s.parentNode.insertBefore(t, s) | |
}(window, | |
document, 'script', 'https://connect.facebook.net/en_US/fbevents.js'); | |
fbq('init', '${fbPixelID}'); | |
fbq('track', 'PageView'); | |
</script> | |
<noscript><img height="1" width="1" style="display:none" | |
src="https://www.facebook.com/tr?id=1803717239894086&ev=PageView&noscript=1" | |
/></noscript>`; | |
}; | |
const transifex = (env, lang) => { | |
const transifexID = config()[getConfig(env)].transifexId; | |
return ` | |
<script type="text/javascript"> | |
window.liveSettings = { | |
api_key: '${transifexID}', | |
picker: "#transifex-selector", | |
detectlang: function() { | |
return '${lang}'; | |
}, | |
autocollect: true, | |
dynamic: true | |
}; | |
</script> | |
<script type="text/javascript" src="//cdn.transifex.com/live.js"></script>`; | |
}; | |
const extraScript = (env, lang) => `${transifex(env, lang)}${fbPixel(env)}${analitycs(env)}`; | |
const injectHTML = (val, { | |
htmlAttrs, bodyAttrs, content, title, meta, link, scripts, state, script, styles, | |
}) => { | |
let data = val.replace(/<script.*?>.*?<\/script>/ig, ''); | |
if (htmlAttrs) data = data.replace(/<html .*?>/, `<html ${htmlAttrs}>`); | |
if (title) data = data.replace(/<title>.*?<\/title>/g, title); | |
if (bodyAttrs) data = data.replace('<body>', `<body ${bodyAttrs}>`); | |
if (meta) data = data.replace('</head>', `${meta}</head>`); | |
if (link) data = data.replace('</head>', `${link}</head>`); | |
if (styles) data = data.replace('</head>', `${styles}</head>`); | |
if (scripts) data = data.replace('</body>', `${scripts.join('')}</body>`); | |
if (script) data = data.replace('</body>', `${script}</body>`); | |
data = data.replace( | |
'<div id="root"></div>', | |
`<div id='root'>${content}${state ? `<script>window.__initialData__ = ${state}</script>` : ''}</div>`, | |
); | |
return data; | |
}; | |
const htmlData = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf8'); | |
const extractAssets = (assets, chunks) => Object.keys(assets) | |
.filter((asset) => chunks.indexOf(asset.replace('.js', '').replace('./')) > -1) | |
.map((k) => assets[k]); | |
export default (req, res) => { | |
const context = {}; | |
const modules = []; | |
const store = configureStore; | |
// isomorphic cookie | |
global.document = { | |
cookie: req.headers.cookie, | |
}; | |
frontloadServerRender(() => renderToString( | |
// eslint-disable-next-line react/jsx-filename-extension | |
<Loadable.Capture report={(m) => modules.push(m)}> | |
<Provider store={store}> | |
<StaticRouter location={req.path} context={context}> | |
<Frontload isServer> | |
<App /> | |
</Frontload> | |
</StaticRouter> | |
</Provider> | |
</Loadable.Capture>, | |
)).then((content) => { | |
if (context.url) { | |
res.redirect(context.url); | |
return res.end(); | |
} | |
const extraChunks = extractAssets(manifest, modules, extraScript).map( | |
(c) => `<script type='text/javascript' src='/${c.replace(/^\//, '')}'></script>`, | |
); | |
const helmet = Helmet.renderStatic(); | |
const htmlAttrs = helmet.htmlAttributes.toString(); | |
const bodyAttrs = helmet.bodyAttributes.toString(); | |
const html = injectHTML(htmlData, { | |
htmlAttrs, | |
bodyAttrs, | |
content, | |
title: helmet.title.toString(), | |
meta: helmet.meta.toString(), | |
link: helmet.link.toString(), | |
scripts: extraChunks, | |
state: JSON.stringify(store.getState()).replace(/</g, '\\u003c'), | |
script: extraScript(getEnviroment(req.headers.host, req).env, 'en'), | |
}); | |
res.send(html); | |
return null; | |
}); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
process.env.NODE_ENV = process.argv[process.argv.length - 1]; | |
process.env.BABEL_ENV = process.argv[process.argv.length - 1]; | |
const path = require('path'); | |
const webpack = require('webpack'); | |
const NodemonPlugin = require('nodemon-webpack-plugin'); | |
const PreloadWebpackPlugin = require('preload-webpack-plugin'); | |
const cssModuleRegex = /\.module\.css$/; | |
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); | |
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); | |
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); | |
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); | |
const ExtractCssChunks = require('extract-css-chunks-webpack-plugin'); | |
const HtmlWebpackPlugin = require('html-webpack-plugin'); | |
const TerserPlugin = require('terser-webpack-plugin'); | |
const WebpackAssetsManifest = require('webpack-assets-manifest'); | |
const CopyPlugin = require('copy-webpack-plugin'); | |
// const devMode = false; | |
const ROOT_DIR = path.resolve(__dirname, './'); | |
const BUILD_DIR = path.join(ROOT_DIR, '../dist'); | |
const devMode = process.argv[process.argv.length - 1] === 'development'; | |
const clientConfig = { | |
mode: 'client', | |
devtool: devMode ? 'inline-source-map' : 'cheap-source-map', | |
entry: [ | |
'@babel/polyfill', | |
'./src/index.jsx', | |
'./src/styles/SCSS/general/main.scss', | |
], | |
output: { | |
path: BUILD_DIR, | |
filename: 'js/[name].bundle.js', | |
chunkFilename: 'js/[name].[hash].js', | |
publicPath: '/', | |
hotUpdateChunkFilename: 'hot/hot-update.[hash].js', | |
hotUpdateMainFilename: 'hot/hot-update.[hash].json', | |
globalObject: 'this' | |
}, | |
stats: { | |
all: devMode, | |
}, | |
module: { | |
rules: [ | |
{ | |
test: /\.(sa|sc|c)ss$/, | |
use: [ | |
{ | |
loader: MiniCssExtractPlugin.loader, | |
options: { | |
hmr: devMode, | |
}, | |
}, | |
{ | |
loader: 'css-loader', | |
options: { | |
sourceMap: devMode, | |
}, | |
}, | |
{ | |
loader: 'resolve-url-loader', | |
options: { | |
debug: true, | |
root: __dirname, | |
}, | |
}, | |
{ | |
loader: 'sass-loader', | |
options: { | |
sourceMap: devMode, | |
}, | |
}, | |
], | |
}, | |
{ | |
test: /\.jsx?/, | |
exclude: /node_modules/, | |
use: { | |
loader: 'babel-loader', | |
options: { | |
presets: ['@babel/preset-env', '@babel/preset-react'], | |
plugins: [ | |
'@babel/plugin-proposal-class-properties', | |
'@babel/plugin-syntax-dynamic-import', | |
'react-loadable/babel', | |
], | |
}, | |
}, | |
}, | |
{ | |
test: /\.svg$/, | |
use: [ | |
{ | |
loader: '@svgr/webpack', | |
}, | |
{ | |
loader: 'file-loader', | |
options: { | |
name: '[name].[ext]', | |
outputPath: 'images/', | |
}, | |
}, | |
], | |
}, | |
{ | |
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/, | |
use: [{ | |
loader: 'file-loader', | |
options: { | |
name: '[name].[ext]', | |
outputPath: 'fonts/', | |
}, | |
}], | |
}, | |
{ | |
test: /\.(jpe?g|png|gif)$/i, | |
use: [{ | |
loader: 'file-loader?name=images/[name].[ext]', | |
options: { | |
esModule: false, | |
}, | |
}], | |
}, | |
{ | |
type: 'javascript/auto', | |
exclude: /(node_modules|bower_components|public)/, | |
test: /\.json$/, | |
use: [ | |
{ | |
loader: 'file-loader', | |
options: { | |
name: 'json/[name].[ext]', | |
}, | |
}, | |
], | |
}, | |
], | |
}, | |
plugins: [ | |
new ExtractCssChunks({ | |
hot: true, | |
}), | |
new MiniCssExtractPlugin({ | |
// Options similar to the same options in webpackOptions.output | |
// both options are optional | |
filename: devMode ? 'css/[name].css' : 'css/[name].[hash].css', | |
chunkFilename: devMode ? 'css/[id].css' : 'css/[id].[hash].css', | |
}), | |
new CleanWebpackPlugin({ | |
dry: false, | |
verbose: true, | |
cleanOnceBeforeBuildPatterns: ['*', '!manifest.json'], | |
dangerouslyAllowCleanPatternsOutsideProject: true, | |
}), | |
// new webpack.HotModuleReplacementPlugin(), | |
new HtmlWebpackPlugin({ | |
hash: true, | |
filename: './index.html', | |
template: './public/index.html', | |
minify: { | |
collapseWhitespace: true, | |
}, | |
}), | |
new webpack.DefinePlugin({ | |
NODE_ENV: JSON.stringify(devMode ? 'development' : 'production'), | |
'process.env': { | |
NODE_ENV: JSON.stringify(devMode ? 'development' : 'production'), | |
}, | |
'process.env.client': true, | |
'process.env.BROWSER': JSON.stringify(true) | |
}), | |
new WebpackAssetsManifest(), | |
], | |
resolve: { | |
extensions: ['.js', '.jsx'], | |
}, | |
optimization: { | |
namedModules: true, | |
namedChunks: true, | |
nodeEnv: devMode ? 'development' : 'production', | |
flagIncludedChunks: true, | |
occurrenceOrder: true, | |
sideEffects: true, | |
usedExports: true, | |
concatenateModules: true, | |
minimize: !devMode, | |
runtimeChunk: { | |
name: 'runtime', | |
}, | |
splitChunks: { | |
chunks: 'async', | |
minSize: 500, | |
maxSize: 0, | |
minChunks: 1, | |
maxAsyncRequests: 5, | |
maxInitialRequests: 3, | |
automaticNameDelimiter: '~', | |
automaticNameMaxLength: 30, | |
name: true, | |
cacheGroups: { | |
vendors: { | |
test: /[\\/]node_modules[\\/]/, | |
priority: -10, | |
}, | |
default: { | |
minChunks: 2, | |
priority: -20, | |
reuseExistingChunk: true, | |
}, | |
styles: { | |
name: 'styles', | |
test: /\.css$/, | |
chunks: 'all', | |
enforce: true, | |
}, | |
}, | |
}, | |
removeAvailableModules: !devMode, | |
noEmitOnErrors: !devMode, | |
checkWasmTypes: false, | |
minimizer: [ | |
new TerserPlugin({ | |
cache: !devMode, | |
parallel: true, | |
sourceMap: devMode, | |
}), | |
new OptimizeCSSAssetsPlugin({ | |
cssProcessorOptions: { | |
map: { | |
inline: !devMode, | |
annotation: true, | |
}, | |
}, | |
}), | |
], | |
}, | |
}; | |
module.exports = [clientConfig]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
process.env.NODE_ENV = process.argv[process.argv.length - 1]; | |
process.env.BABEL_ENV = process.argv[process.argv.length - 1]; | |
const path = require('path'); | |
const webpack = require('webpack'); | |
const NodemonPlugin = require('nodemon-webpack-plugin'); | |
const PreloadWebpackPlugin = require('preload-webpack-plugin'); | |
const cssModuleRegex = /\.module\.css$/; | |
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); | |
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); | |
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); | |
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); | |
const ExtractCssChunks = require('extract-css-chunks-webpack-plugin'); | |
const HtmlWebpackPlugin = require('html-webpack-plugin'); | |
const TerserPlugin = require('terser-webpack-plugin'); | |
const WebpackAssetsManifest = require('webpack-assets-manifest'); | |
const CopyPlugin = require('copy-webpack-plugin'); | |
// const devMode = false; | |
const ROOT_DIR = path.resolve(__dirname, './'); | |
const BUILD_DIR = path.join(ROOT_DIR, '../dist'); | |
const devMode = process.argv[process.argv.length - 1] === 'development'; | |
const serverConfig = { | |
mode: 'server', | |
entry: ['@babel/polyfill', `${__dirname}/index.js`], | |
target: 'node', | |
node: { | |
__filename: false, | |
__dirname: false, | |
}, | |
stats: { | |
all: devMode, | |
}, | |
output: { | |
path: path.resolve(__dirname, '../dist'), | |
filename: 'server.js', | |
chunkFilename: 'js/server/[name].js', | |
publicPath: '/', | |
globalObject: 'this' | |
}, | |
plugins: [ | |
new NodemonPlugin({ | |
verbose: true, | |
watch: path.resolve('./dist'), | |
script: './dist/server.js', | |
// Extensions to watch | |
ext: 'js,njk,json', | |
}), | |
new webpack.optimize.LimitChunkCountPlugin({ | |
maxChunks: 1, | |
}), | |
new webpack.DefinePlugin({ | |
__CLIENT__: true, | |
NODE_ENV: JSON.stringify(devMode ? 'development' : 'production'), | |
'process.env': { | |
NODE_ENV: JSON.stringify(devMode ? 'development' : 'production'), | |
}, | |
'process.env.client': false, | |
}), | |
], | |
resolve: { | |
extensions: ['.js', '.jsx'], | |
}, | |
module: { | |
rules: [ | |
{ | |
test: /\.(js|jsx)$/, | |
exclude: /node_modules/, | |
use: { | |
loader: 'babel-loader', | |
options: { | |
presets: ['@babel/preset-env', '@babel/preset-react'], | |
plugins: [ | |
'@babel/plugin-proposal-class-properties', | |
'@babel/plugin-syntax-dynamic-import', | |
'react-loadable/babel', | |
], | |
}, | |
}, | |
}, | |
{ | |
test: cssModuleRegex, | |
use: [ | |
{ | |
loader: 'css-loader/locals', | |
options: { | |
modules: true, | |
getLocalIdent: getCSSModuleLocalIdent, | |
}, | |
}, | |
], | |
}, | |
{ test: /\.css$/, exclude: /\.module.css$/, loader: 'ignore-loader' }, | |
{ | |
test: /\.svg$/, | |
use: ['@svgr/webpack', 'url-loader'], | |
}, | |
{ | |
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/, | |
use: [{ | |
loader: 'file-loader', | |
options: { | |
name: '[name].[ext]', | |
outputPath: 'fonts/', | |
}, | |
}], | |
}, | |
{ | |
test: /\.(jpe?g|png|gif)$/i, | |
use: [ | |
'file-loader?name=images/[name].[ext]', | |
], | |
}, | |
{ | |
// Loads the javacript into html template provided. | |
// Entry point is set below in HtmlWebPackPlugin in Plugins | |
test: /\.html$/, | |
use: [ | |
{ | |
loader: 'html-loader', | |
}, | |
], | |
}, | |
], | |
}, | |
}; | |
module.exports = [serverConfig]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment