Skip to content

Commit

Permalink
add async-node example (#575)
Browse files Browse the repository at this point in the history
  • Loading branch information
7rulnik authored Oct 14, 2020
1 parent b4151d8 commit 63fd151
Show file tree
Hide file tree
Showing 22 changed files with 337 additions and 0 deletions.
9 changes: 9 additions & 0 deletions examples/server-side-rendering-async-node/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"env": {
"browser": true
},
"rules": {
"import/no-unresolved": "off",
"import/no-extraneous-dependencies": "off"
}
}
1 change: 1 addition & 0 deletions examples/server-side-rendering-async-node/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public/dist
35 changes: 35 additions & 0 deletions examples/server-side-rendering-async-node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Get the SSR example running

Steps:

1. Download repository

```bash
git clone https://github.com/gregberge/loadable-components.git
```

2. move into example directory

```bash
cd ./loadable-components/examples/server-side-rendering-async-node
```

3. install [https://yarnpkg.com/lang/en/docs/install](yarn) if haven't already
4. install project dependencies

```bash
yarn
```

5. run locally or build and serve

```bash
yarn dev

# Or

yarn build
yarn start
```

🍻
28 changes: 28 additions & 0 deletions examples/server-side-rendering-async-node/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
function isWebTarget(caller) {
return Boolean(caller && caller.target === 'web')
}

function isWebpack(caller) {
return Boolean(caller && caller.name === 'babel-loader')
}

module.exports = api => {
const web = api.caller(isWebTarget)
const webpack = api.caller(isWebpack)

return {
presets: [
'@babel/preset-react',
[
'@babel/preset-env',
{
useBuiltIns: web ? 'entry' : undefined,
corejs: web ? 'core-js@3' : false,
targets: !web ? { node: 'current' } : undefined,
modules: webpack ? false : 'commonjs',
},
],
],
plugins: ['@babel/plugin-syntax-dynamic-import', '@loadable/babel-plugin'],
}
}
6 changes: 6 additions & 0 deletions examples/server-side-rendering-async-node/nodemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"ignore": ["client", "public"],
"execMap": {
"js": "babel-node"
}
}
36 changes: 36 additions & 0 deletions examples/server-side-rendering-async-node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"private": true,
"scripts": {
"dev": "nodemon src/server/main.js",
"build": "NODE_ENV=production yarn build:webpack && yarn build:lib",
"build:webpack": "webpack",
"build:lib": "babel -d lib src",
"start": "NODE_ENV=production node lib/server/main.js"
},
"devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.6.2",
"@babel/node": "^7.0.0",
"@babel/preset-env": "^7.6.2",
"@babel/preset-react": "^7.0.0",
"@loadable/babel-plugin": "^5.10.3",
"@loadable/component": "^5.10.3",
"@loadable/server": "^5.10.3",
"@loadable/webpack-plugin": "^5.7.1",
"babel-loader": "^8.0.6",
"css-loader": "^2.1.1",
"mini-css-extract-plugin": "^0.6.0",
"nodemon": "^1.19.0",
"webpack": "^5.0.0-beta.16",
"webpack-cli": "^3.3.2",
"webpack-dev-middleware": "^3.6.2",
"webpack-node-externals": "^1.7.2"
},
"dependencies": {
"core-js": "^3.0.1",
"express": "^4.16.4",
"moment": "^2.24.0",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}
55 changes: 55 additions & 0 deletions examples/server-side-rendering-async-node/src/client/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react'
// eslint-disable-next-line import/no-extraneous-dependencies
import loadable from '@loadable/component'
import './main.css'

const A = loadable(() => import('./letters/A'))
const B = loadable(() => import('./letters/B'))
const C = loadable(() => import(/* webpackPreload: true */ './letters/C'))
const D = loadable(() => import(/* webpackPrefetch: true */ './letters/D'))
const E = loadable(() => import('./letters/E'), { ssr: false })
const X = loadable(props => import(`./letters/${props.letter}`))
const Sub = loadable(props => import(`./letters/${props.letter}/file`))
const RootSub = loadable(props => import(`./${props.letter}/file`))

// Load the 'G' component twice: once in SSR and once fully client-side
const GClient = loadable(() => import('./letters/G'), {
ssr: false,
fallback: <span className="loading-state">ssr: false - Loading...</span>,
})
const GServer = loadable(() => import('./letters/G'), {
ssr: true,
fallback: <span className="loading-state">ssr: true - Loading...</span>,
})

// We keep some references to prevent uglify remove
A.C = C
A.D = D

const Moment = loadable.lib(() => import('moment'))

const App = () => (
<div>
<A />
<br />
<B />
<br />
<X letter="A" />
<br />
<X letter="F" />
<br />
<E />
<br />
<GClient prefix="ssr: false" />
<br />
<GServer prefix="ssr: true" />
<br />
<Sub letter="Z" />
<br />
<RootSub letter="Y" />
<br />
<Moment>{moment => moment().format('HH:mm')}</Moment>
</div>
)

export default App
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => 'Y'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* A CSS */
10 changes: 10 additions & 0 deletions examples/server-side-rendering-async-node/src/client/letters/A.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// We simulate that "moment" is called in "A" and "B"
import moment from 'moment'
import './A.css'

const A = () => 'A'

// We keep a reference to prevent uglify remove
A.moment = moment

export default A
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// We simulate that "moment" is called in "A" and "B"
import moment from 'moment'

const B = () => 'B'

// We keep a reference to prevent uglify remove
B.moment = moment

export default B
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => 'C'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => 'D'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => 'E'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => 'F'
11 changes: 11 additions & 0 deletions examples/server-side-rendering-async-node/src/client/letters/G.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react'

const G = ({prefix}) => (
<span className="my-cool-class">
{prefix}
{' '}
- G
</span>
)

export default G
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => 'Z'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './App'
11 changes: 11 additions & 0 deletions examples/server-side-rendering-async-node/src/client/main-web.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'core-js'
import React from 'react'
import { hydrate } from 'react-dom'
// eslint-disable-next-line import/no-extraneous-dependencies
import { loadableReady } from '@loadable/component'
import App from './App'

loadableReady(() => {
const root = document.getElementById('main')
hydrate(<App />, root)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Main CSS */
67 changes: 67 additions & 0 deletions examples/server-side-rendering-async-node/src/server/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import path from 'path'
import express from 'express'
import React from 'react'
import { renderToString } from 'react-dom/server'
import { ChunkExtractor } from '@loadable/server'

const app = express()

app.use(express.static(path.join(__dirname, '../../public')))

if (process.env.NODE_ENV !== 'production') {
/* eslint-disable global-require, import/no-extraneous-dependencies */
const { default: webpackConfig } = require('../../webpack.config.babel')
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
/* eslint-enable global-require, import/no-extraneous-dependencies */

const compiler = webpack(webpackConfig)

app.use(
webpackDevMiddleware(compiler, {
logLevel: 'silent',
publicPath: '/dist/web',
writeToDisk(filePath) {
return /dist\/node\//.test(filePath) || /loadable-stats/.test(filePath)
},
}),
)
}

const nodeStats = path.resolve(
__dirname,
'../../public/dist/async-node/loadable-stats.json',
)

const webStats = path.resolve(
__dirname,
'../../public/dist/web/loadable-stats.json',
)

app.get('*', (req, res) => {
const nodeExtractor = new ChunkExtractor({ statsFile: nodeStats })
const { default: App } = nodeExtractor.requireEntrypoint()

const webExtractor = new ChunkExtractor({ statsFile: webStats })
const jsx = webExtractor.collectChunks(<App />)

const html = renderToString(jsx)

res.set('content-type', 'text/html')
res.send(`
<!DOCTYPE html>
<html>
<head>
${webExtractor.getLinkTags()}
${webExtractor.getStyleTags()}
</head>
<body>
<div id="main">${html}</div>
${webExtractor.getScriptTags()}
</body>
</html>
`)
})

// eslint-disable-next-line no-console
app.listen(9000, () => console.log('Server started http://localhost:9000'))
50 changes: 50 additions & 0 deletions examples/server-side-rendering-async-node/webpack.config.babel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import path from 'path'
import nodeExternals from 'webpack-node-externals'
import LoadablePlugin from '@loadable/webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'

const DIST_PATH = path.resolve(__dirname, 'public/dist')
const production = process.env.NODE_ENV === 'production'
const development =
!process.env.NODE_ENV || process.env.NODE_ENV === 'development'

const getConfig = target => ({
name: target,
mode: development ? 'development' : 'production',
target,
entry: `./src/client/main-${target}.js`,
module: {
rules: [
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
caller: { target },
},
},
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
],
},
],
},
externals:
target === 'async-node' ? ['@loadable/component', nodeExternals()] : undefined,
output: {
path: path.join(DIST_PATH, target),
filename: production ? '[name]-bundle-[chunkhash:8].js' : '[name].js',
publicPath: `/dist/${target}/`,
libraryTarget: target === 'async-node' ? 'commonjs2' : undefined,
},
plugins: [new LoadablePlugin(), new MiniCssExtractPlugin()],
})

export default [getConfig('web'), getConfig('async-node')]

0 comments on commit 63fd151

Please sign in to comment.