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:
parent
b92cd536d3
commit
26e86fc144
52
README.md
52
README.md
@ -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
|
||||
|
28
bin/cli.js
28
bin/cli.js
@ -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)
|
||||
|
25
docs/App.js
25
docs/App.js
@ -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
3
docs/index.html
Normal 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">{
|
||||
"count": 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
11
docs/pages/about.js
Normal 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
11
docs/pages/home.js
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const Home = props => (
|
||||
<div>
|
||||
<h1>
|
||||
Home
|
||||
</h1>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Home
|
@ -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,
|
||||
|
@ -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__',
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -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':
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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": []
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user