Skip to content

Commit

Permalink
Merge pull request #1680 from SUI-Components/feat/sui-ssr-dev
Browse files Browse the repository at this point in the history
feat(packages/sui-ssr): add ssr dev mode
  • Loading branch information
andresz1 authored Dec 13, 2023
2 parents 15f0341 + f2f6fd9 commit 830ea28
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 23 deletions.
57 changes: 36 additions & 21 deletions packages/sui-ssr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,40 @@ SSR can be tought to configure and maintain. SSR handles that for you providing:
npm install @s-ui/ssr --save
```

## Development

Starts a development server.

```
Usage: sui-ssr-dev [options]
Options:
-L, --link-all [monorepo] Link all packages inside of monorepo multipackage
-l, --link-package [package] Replace each occurrence of this package with an absolute path to this folder (default: [])
-h, --help display help for command
Examples:
$ sui-ssr dev
$ sui-ssr dev --link-package ./domain --link-all ./components
```

## Build

Generate a static version of the server w/out dependencies in the server folder.

```
Usage: sui-ssr-build [options]
Options:
Usage: sui-ssr-build [options]
-C, --clean Remove build folder before create a new one
-V, --verbose Verbose output
-h, --help output usage information
Description:
Options:
Build a production ready ssr server
-C, --clean Remove build folder before create a new one
-V, --verbose Verbose output
-h, --help output usage information
Examples:
Examples:
$ sui-ssr build
$ sui-ssr build
```

## Archive
Expand All @@ -41,21 +56,21 @@ Create a zip file with all assets needed to run the server in any infra.
It will, over parameter, make that the express server run over a username and password in a htpasswd way.

```
Usage: sui-ssr-archive [options]
Usage: sui-ssr-archive [options]
Options:
Options:
-C, --clean Remove previous zip
-R, --docker-registry <dockerRegistry> Custom registry to be used as a proxy or instead of the Docker Hub registry
-E, --entry-point Relative path to an entry point script to replace the current one -> https://bit.ly/3e4wT8C
-h, --help Output usage information
-A, --auth <username:password> Will build the express definition under authentication htpassword like.
-O, --outputFileName <outputFileName> A string that will be used to set the name of the output filename. Keep in mind that the outputFilename will have the next suffix <outputFileName>-sui-ssr.zip
-C, --clean Remove previous zip
-R, --docker-registry <dockerRegistry> Custom registry to be used as a proxy or instead of the Docker Hub registry
-E, --entry-point Relative path to an entry point script to replace the current one -> https://bit.ly/3e4wT8C
-h, --help Output usage information
-A, --auth <username:password> Will build the express definition under authentication htpassword like.
-O, --outputFileName <outputFileName> A string that will be used to set the name of the output filename. Keep in mind that the outputFilename will have the next suffix <outputFileName>-sui-ssr.zip
Examples:
Examples:
$ sui-ssr archive
$ sui-ssr archive --outputFileName=myFile // output: myFile-sui-ssr.zip
$ sui-ssr archive
$ sui-ssr archive --outputFileName=myFile // output: myFile-sui-ssr.zip
```

### IMPORTANT!!
Expand Down
160 changes: 160 additions & 0 deletions packages/sui-ssr/bin/sui-ssr-dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/usr/bin/env node
/* eslint-disable no-console */

const {WEBPACK_PORT = 8080} = process.env
process.env.CDN = `http://localhost:${WEBPACK_PORT}/`

const program = require('commander')
const {exec} = require('child_process')
const path = require('path')
const fs = require('fs')
const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpackHotMiddleware = require('webpack-hot-middleware')
const nodemon = require('nodemon')
const clientConfig = require('@s-ui/bundler/webpack.config.server.dev.js')
const linkLoaderConfigBuilder = require('@s-ui/bundler/loaders/linkLoaderConfigBuilder.js')

const serverConfigFactory = require('../compiler/server.js')

const TMP_PATH = '.sui'
const SERVER_OUTPUT_PATH = path.join(process.cwd(), `${TMP_PATH}/server`)
const STATICS_PATH = path.join(process.cwd(), './statics')
const STATICS_OUTPUT_PATH = path.join(process.cwd(), `${TMP_PATH}/statics`)

program
.option('-L, --link-all [monorepo]', 'Link all packages inside of monorepo multipackage')
.option(
'-l, --link-package [package]',
'Replace each occurrence of this package with an absolute path to this folder',
(v, m) => {
m.push(v)
return m
},
[]
)
.parse(process.argv)

const compile = (name, compiler) => {
return new Promise((resolve, reject) => {
compiler.hooks.compile.tap(name, () => {
console.time(`[${name}] Compiling`)
})
compiler.hooks.done.tap(name, stats => {
console.timeEnd(`[${name}] Compiling`)
if (!stats.hasErrors()) {
return resolve()
}
return reject(new Error(`Failed to compile ${name}`))
})
})
}

const linkStatics = () => {
return new Promise((resolve, reject) =>
fs.symlink(STATICS_PATH, STATICS_OUTPUT_PATH, 'dir', err => {
if (err) {
if (err.code === 'EEXIST') {
return resolve()
}

return reject(err)
}

resolve()
})
)
}

const initMSW = () => {
return exec(`npx msw init ${STATICS_PATH}`)
}

const start = ({packagesToLink, linkAll}) => {
const app = express()
const clientCompiler = webpack(
linkLoaderConfigBuilder({
config: require('@s-ui/bundler/webpack.config.server.dev.js'),
linkAll,
packagesToLink
})
)
const serverCompiler = webpack(
linkLoaderConfigBuilder({
config: serverConfigFactory({outputPath: SERVER_OUTPUT_PATH}),
linkAll,
packagesToLink
})
)
const watchOptions = {
ignored: /node_modules/,
stats: clientConfig.stats
}

app.use((_req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
return next()
})

app.use(
webpackDevMiddleware(clientCompiler, {
publicPath: clientConfig.output.publicPath,
stats: clientConfig.stats,
writeToDisk: true
})
)
app.use(webpackHotMiddleware(clientCompiler))

app.listen(WEBPACK_PORT)

serverCompiler.watch(watchOptions, (error, stats) => {
if (!error && !stats.hasErrors()) {
return
}

if (error) {
console.log(error, 'error')
}

if (stats.hasErrors()) {
const info = stats.toJson()
const errors = info.errors

console.log(errors)
}
})

if (!fs.existsSync(TMP_PATH)) {
fs.mkdirSync(TMP_PATH)
}

Promise.all([linkStatics(), initMSW(), compile('client', clientCompiler), compile('server', serverCompiler)])
.then(() => {
const script = nodemon({
script: `${SERVER_OUTPUT_PATH}/index.js`,
watch: [SERVER_OUTPUT_PATH],
delay: 200
})

script.on('restart', () => {
console.log('Server side app has been restarted.', 'warning')
})

script.on('quit', () => {
console.log('Process ended')
process.exit()
})

script.on('error', () => {
console.log('An error occured. Exiting', 'error')
process.exit(1)
})
})
.catch(error => {
console.log('error', error)
})
}
const opts = program.opts()

start({packagesToLink: opts.linkPackage, linkAll: opts.linkAll})
1 change: 1 addition & 0 deletions packages/sui-ssr/bin/sui-ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ program.version(version, ' --version')
program.command('build', 'Build a ssr server').alias('b')
program.command('archive', 'Create a server.zip file').alias('a')
program.command('release', 'Release new version of the server').alias('r')
program.command('dev', 'Start a ssr server in development mode').alias('d')

program.parse(process.argv)
5 changes: 4 additions & 1 deletion packages/sui-ssr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
"mime": "1.6.0",
"noop-console": "0.8.0",
"parse5": "6.0.1",
"ua-parser-js": "0.7.33"
"ua-parser-js": "0.7.33",
"webpack-dev-middleware": "6.1.1",
"webpack-hot-middleware": "2.25.4",
"nodemon": "3.0.1"
}
}
10 changes: 9 additions & 1 deletion packages/sui-ssr/server/utils/factory.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const DEFAULT_SITE_HEADER = 'X-Serve-Site'
const DEFAULT_PUBLIC_FOLDER = 'public'
const DEFAULT_DEV_PUBLIC_FOLDER = '.sui/public'
const DEFAULT_MULTI_SITE_KEY = 'default'
const EXPRESS_STATIC_CONFIG = {index: false}

Expand Down Expand Up @@ -28,8 +29,15 @@ export default ({path, fs, config: ssrConf = {}, assetsManifest}) => {
}

const publicFolder = req => {
if (process.env.NODE_ENV !== 'production') {
return DEFAULT_DEV_PUBLIC_FOLDER
}

const site = siteByHost(req)
if (!site) return DEFAULT_PUBLIC_FOLDER

if (!site) {
return DEFAULT_PUBLIC_FOLDER
}

return multiSitePublicFolder(site)
}
Expand Down

0 comments on commit 830ea28

Please sign in to comment.