1
1
mirror of https://github.com/c8r/x0.git synced 2024-09-11 21:57:26 +03:00

Client side bundles

This commit is contained in:
Brent Jackson 2017-09-13 16:17:26 -04:00
parent b92cd536d3
commit 26e86fc144
12 changed files with 228 additions and 56 deletions

View File

@ -32,16 +32,32 @@ Options:
```sh
rx0 static src/App.js
```
Render with a custom root HTML component to control CSS, routing, etc.
```
rx0 static src/App.js src/Html.js
```
Options
- css lib?
- out-dir
- client-bundle
- json props
### Configuration
In `package.json`
```json
"rx0": {
"title": "Hello",
"props": {
"count": 0
}
}
```
MIT License
---
@ -55,22 +71,21 @@ To do:
- [x] render static
- [x] render static callback
- [ ] render css (yikes)
- [x] render json
- [x] rehydrate from json
- [ ] render bundle
- [ ] rehydrate from json
- [ ] out-dir option
- [x] render bundle
- [x] rehydrate from json
- [x] out-dir option
- [ ] render css (yikes)
- [x] configurable (props) html render
- [x] default html render
- [x] merge options with package.json
- [x] update-notifier
- [ ] render multiple routes
- [ ] react router?
- [ ] router components
- [ ] readdir
- [ ] configuration
- [ ] update-notifier
- [ ] configuration `options.pages`
- User configures routing in App ssr/static props for current pathname hydration
---
@ -84,23 +99,6 @@ pages: [
]
```
```jsx
const IndexPage = props => (
<Route path='/'>
<Main />
</Route>
)
const AboutPage = props => (
<Route path='/about'>
<About />
</Route>
)
```
```sh
rx0 src/pages/
```
```sh
io
iox

View File

@ -3,6 +3,11 @@
const fs = require('fs')
const path = require('path')
const meow = require('meow')
const { pkg } = require('read-pkg-up').sync()
require('update-notifier')({
pkg: require('../package.json')
}).notify()
const cli = meow(`
Usage:
@ -11,17 +16,25 @@ const cli = meow(`
$ rx0 static src/Component.js
Options:
-d --out-dir Output directory for static build
`, {
alias: {
d: 'outDir'
}
})
const [ cmd, file, rootpath ] = cli.input
const options = cli.flags
const options = Object.assign({}, pkg.rx0, cli.flags)
const absolute = f => f
? path.isAbsolute(f) ? f : path.join(process.cwd(), f)
: null
const filename = absolute(file)
const dir = fs.statSync(filename).isDirectory()
const root = absolute(rootpath)
switch (cmd) {
@ -33,8 +46,17 @@ switch (cmd) {
break
case 'static':
const static = require('../lib/static')
const html = static(filename, root, options)
console.log(html)
if (dir) {
console.log('todo: render multiple pages')
} else {
static(filename, root, options, (err, html) => {
if (!options.outDir) {
console.log(html)
} else {
console.log('Static page built')
}
})
}
break
default:
process.exit(0)

View File

@ -1,14 +1,31 @@
import React from 'react'
import cxs from 'cxs/component'
import { createProvider } from 'refunk'
const Title = cxs('h1')({
color: 'tomato'
})
const dec = state => ({ count: state.count - 1 })
const inc = state => ({ count: state.count + 1 })
const Debug = props => <pre children={JSON.stringify(props, null, 2)} />
const App = props => (
<Title>
Hello
</Title>
<div>
<Title>
Hello {props.count}
</Title>
<Debug {...props} />
<button
onClick={e => props.update(dec)}
children='-'
/>
<button
onClick={e => props.update(inc)}
children='+'
/>
</div>
)
export default App
export default createProvider({})(App)

3
docs/index.html Normal file
View File

@ -0,0 +1,3 @@
<!DOCTYPE html><html data-reactroot="" data-reactid="1" data-react-checksum="-1242947289"><head data-reactid="2"><title data-reactid="3">RX0</title><meta charset="utf-8" data-reactid="4"/><meta name="viewport" content="width=device-width, initial-scale=1" data-reactid="5"/><meta name="description" data-reactid="6"/><style data-reactid="7">.x0{color:tomato}</style></head><body data-reactid="8"><div id="app" data-reactid="9"><div data-reactroot="" data-reactid="1" data-react-checksum="781606405"><h1 class="x0" data-reactid="2"><!-- react-text: 3 -->Hello <!-- /react-text --><!-- react-text: 4 -->2<!-- /react-text --></h1><pre data-reactid="5">{
&quot;count&quot;: 2
}</pre><button data-reactid="6">-</button><button data-reactid="7">+</button></div></div><script id="__initial-props__" type="application/json" data-reactid="10">{"count":2}</script><script src="bundle.js" data-reactid="11"></script></body></html>

11
docs/pages/about.js Normal file
View File

@ -0,0 +1,11 @@
import React from 'react'
const About = props => (
<div>
<h1>
About
</h1>
</div>
)
export default About

11
docs/pages/home.js Normal file
View File

@ -0,0 +1,11 @@
import React from 'react'
const Home = props => (
<div>
<h1>
Home
</h1>
</div>
)
export default Home

View File

@ -4,8 +4,6 @@ const DevServer = require('webpack-dev-server')
const open = require('opn')
const config = require('./config')
const { pkg } = require('read-pkg-up').sync()
const devOptions = {
hot: true,
historyApiFallback: {
@ -22,7 +20,7 @@ module.exports = (filename, options = {}, cb) => {
const {
port = 8000,
props = {}
} = Object.assign({}, pkg.rx0, options)
} = options
config.resolve.modules.unshift(
dirname,

View File

@ -7,6 +7,7 @@ module.exports = ({
image,
css,
js,
bundle,
stylesheets = [],
scripts = [],
initialProps,
@ -22,9 +23,12 @@ module.exports = ({
stylesheets.map(href => h('link', { key: href, href, rel: 'stylesheet' })),
),
h('body', null,
h('div', { id: 'app' },
children
),
h('div', {
id: 'app',
dangerouslySetInnerHTML: {
__html: children
}
}),
// todo: boolean condition
h('script', {
id: '__initial-props__',

View File

@ -1,3 +1,89 @@
// todo:
// - webpack bundle
module.exports = (filename, options) => {}
const path = require('path')
const webpack = require('webpack')
const config = {
entry: [
path.join(__dirname, './entry')
],
output: {
filename: 'bundle.js'
},
resolve: {
modules: [
path.join(__dirname, '../../node_modules'),
'node_modules'
]
},
module: {
rules: [
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: require.resolve('babel-loader'),
options: {
presets: [
'babel-preset-env',
'babel-preset-stage-0',
'babel-preset-react'
].map(require.resolve)
}
}
}
]
},
plugins: [
]
}
module.exports = (filename, options = {}) => {
const {
outDir,
} = options
if (!outDir) return
const dirname = path.dirname(filename)
config.output.path = path.join(process.cwd(), outDir)
config.entry.push()
config.resolve.modules.unshift(
dirname,
path.join(process.cwd(), 'node_modules'),
path.join(dirname, 'node_modules'),
)
config.plugins.push(
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
},
COMPONENT: JSON.stringify(filename)
})
)
config.plugins.push(
new webpack.optimize.UglifyJsPlugin()
)
config.plugins.push(
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
})
)
const compiler = webpack(config)
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
if (err) {
console.log(err)
reject(err)
}
console.log(stats)
resolve(stats)
})
})
}

View File

@ -1,5 +1,6 @@
const { pkg } = require('read-pkg-up').sync()
// all these libraries might be wildly too different to reasonably account for
const libs = [
'styled-components',
'cxs',
@ -21,14 +22,18 @@ module.exports = () => {
}
})
// all these libraries might be wildly too different to reasonably account for
switch (lib) {
case 'styled-components':
// wtf this api is convoluted af
// seems like this needs the components to render inside
// one of these functions
const { ServerStyleSheet } = require('styled-components')
const sheet = new ServerStyleSheet()
// console.log('sheet instance', sheet.create)
// const css = sheet.getStyleTags()
// could return react elements here
// const css = sheet.getStyleElements()
break
// case 'glamorous':

View File

@ -5,13 +5,13 @@ require('babel-register')({
'babel-preset-react'
].map(require.resolve)
})
const fs = require('fs')
const path = require('path')
const React = require('react')
const { renderToString } = require('react-dom/server')
const Html = require('./Html')
const renderCSS = require('./css')
const { pkg } = require('read-pkg-up').sync()
const client = require('./client')
// const renderCSS = require('./css')
const render = (Component, props) => renderToString(
React.createElement(Component, props)
@ -19,11 +19,11 @@ const render = (Component, props) => renderToString(
const doc = n => '<!DOCTYPE html>' + n
module.exports = (filename, root, options = {}, cb) => {
const opts = Object.assign({}, pkg.rx0, options)
module.exports = async (filename, root, options = {}, cb) => {
const {
props = {}
} = opts
props = {},
routes
} = options
const req = require(filename)
const Component = req.default || req
@ -31,10 +31,16 @@ module.exports = (filename, root, options = {}, cb) => {
const body = render(Component, props)
const stats = await client(filename, options)
// probably just delegate this to custom Root components
// const css = renderCSS(Component, props)
const rootProps = Object.assign({}, opts, props, {
const rootProps = Object.assign({}, options, props, {
// css,
scripts: [
'bundle.js'
],
initialProps: JSON.stringify(props),
children: body
})
@ -42,7 +48,13 @@ module.exports = (filename, root, options = {}, cb) => {
? render(Root, rootProps)
: render(Html, rootProps)
if (typeof cb === 'function') cb(null, doc(html))
if (typeof cb === 'function') {
if (options.outDir) {
const name = path.join(process.cwd(), options.outDir, 'index.html')
fs.writeFileSync(name, doc(html))
}
cb(null, doc(html))
}
return doc(html)
}

View File

@ -9,7 +9,7 @@
"scripts": {
"start": "./bin/cli.js dev docs/App.js",
"build": "./bin/cli.js static docs/App.js",
"test": "./bin/cli.js static docs/App.js docs/Root.js"
"test": "./bin/cli.js static docs/App.js docs/Root.js -d docs"
},
"keywords": [],
"author": "Brent Jackson",
@ -26,14 +26,19 @@
"react": "^15.6.1",
"react-dom": "^15.6.1",
"read-pkg-up": "^2.0.0",
"update-notifier": "^2.2.0",
"webpack": "^3.5.6",
"webpack-dev-server": "^2.7.1"
},
"devDependencies": {
"cxs": "^6.0.0"
"cxs": "^6.0.0",
"refunk": "^1.0.0-2"
},
"rx0": {
"title": "RX0",
"props": {
"count": 2
},
"pages": []
}
}