mirror of
https://github.com/c8r/x0.git
synced 2024-08-16 17:00:24 +03:00
Add scope and matching
This commit is contained in:
parent
a1ddd164fe
commit
74818b7066
14
README.md
14
README.md
@ -367,9 +367,13 @@ See the [example](https://github.com/c8r/x0/tree/master/examples/webpack-config)
|
||||
|
||||
**REMOVE BEFORE MERGING**
|
||||
|
||||
- [ ] deep require context
|
||||
- [ ] minimatch
|
||||
- [ ] default route sorting
|
||||
- [ ] markdown scope
|
||||
- [ ] move client modules to src
|
||||
- [ ] adjust resolve
|
||||
- [ ] default route sorting
|
||||
- [x] deep require context
|
||||
- [ ] route dirname/full path
|
||||
- [ ] minimatch
|
||||
- [ ] pass front-matter as props
|
||||
- [ ] props.Component in custom apps
|
||||
- [x] move client modules to src
|
||||
- [x] adjust resolve
|
||||
|
||||
|
4
cli.js
4
cli.js
@ -28,6 +28,7 @@ const cli = meow(`
|
||||
${chalk.gray('Options')}
|
||||
|
||||
--webpack Path to webpack config file
|
||||
--match String to match routes against using minimatch
|
||||
|
||||
${chalk.gray('Dev Server')}
|
||||
|
||||
@ -68,6 +69,9 @@ const cli = meow(`
|
||||
type: 'string',
|
||||
alias: 'c'
|
||||
},
|
||||
match: {
|
||||
type: 'string'
|
||||
},
|
||||
scope: {
|
||||
type: 'string',
|
||||
},
|
||||
|
36
docs/_app.js
Normal file
36
docs/_app.js
Normal file
@ -0,0 +1,36 @@
|
||||
import React from 'react'
|
||||
import * as scope from 'rebass'
|
||||
import { ScopeProvider } from '../src'
|
||||
import {
|
||||
Flex,
|
||||
Box,
|
||||
Container,
|
||||
} from 'rebass'
|
||||
|
||||
export default class App extends React.Component {
|
||||
static defaultProps = {
|
||||
title: 'Hello'
|
||||
}
|
||||
|
||||
render () {
|
||||
const { render } = this.props
|
||||
console.log('custom app', this.props.routes)
|
||||
|
||||
return (
|
||||
<ScopeProvider scope={scope}>
|
||||
<Flex>
|
||||
{false && (
|
||||
<Box p={2} flex='none' width={192}>
|
||||
custom app
|
||||
</Box>
|
||||
)}
|
||||
<Box width={1} p={3}>
|
||||
<Container maxWidth={768}>
|
||||
{render()}
|
||||
</Container>
|
||||
</Box>
|
||||
</Flex>
|
||||
</ScopeProvider>
|
||||
)
|
||||
}
|
||||
}
|
@ -9,5 +9,5 @@ This is a standard markdown file.
|
||||
This is a live/editable code block:
|
||||
|
||||
```.jsx
|
||||
<h1>Hello</h1>
|
||||
<Box p={4} bg='tomato'>Hello</Box>
|
||||
```
|
||||
|
6
docs/examples/Button.md
Normal file
6
docs/examples/Button.md
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
# Button
|
||||
|
||||
```.jsx
|
||||
<Button>Button</Button>
|
||||
```
|
2
docs/examples/index.md
Normal file
2
docs/examples/index.md
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
# Examples
|
@ -165,7 +165,8 @@ module.exports = async (opts) => {
|
||||
DEV: JSON.stringify(false),
|
||||
OPTIONS: JSON.stringify(opts),
|
||||
DIRNAME: JSON.stringify(opts.dirname),
|
||||
APP: JSON.stringify(opts.app)
|
||||
APP: JSON.stringify(opts.app),
|
||||
MATCH: JSON.stringify(opts.match)
|
||||
})
|
||||
)
|
||||
|
||||
|
@ -50,6 +50,7 @@ module.exports = async (opts) => {
|
||||
OPTIONS: JSON.stringify(opts),
|
||||
DIRNAME: JSON.stringify(opts.dirname),
|
||||
APP: JSON.stringify(opts.app),
|
||||
MATCH: JSON.stringify(opts.match)
|
||||
})
|
||||
)
|
||||
|
||||
|
@ -30,6 +30,8 @@
|
||||
"@compositor/log": "^1.0.0-0",
|
||||
"@mdx-js/loader": "^0.11.0",
|
||||
"@mdx-js/mdx": "^0.10.1",
|
||||
"@mdx-js/tag": "^0.11.0",
|
||||
"@rebass/markdown": "^1.0.0-1",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-plugin-macros": "^2.2.2",
|
||||
@ -50,12 +52,14 @@
|
||||
"koa-connect": "^2.0.1",
|
||||
"meow": "^5.0.0",
|
||||
"mini-html-webpack-plugin": "^0.2.3",
|
||||
"minimatch": "^3.0.4",
|
||||
"pkg-conf": "^2.1.0",
|
||||
"react": "^16.4.1",
|
||||
"react-dev-utils": "^5.0.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-router": "^4.3.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-scope-provider": "^1.0.0-1",
|
||||
"read-pkg-up": "^3.0.0",
|
||||
"remark-autolink-headings": "^5.0.0",
|
||||
"remark-emoji": "^2.0.1",
|
||||
@ -69,13 +73,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@compositor/logo": "^1.4.0",
|
||||
"@compositor/md-loader": "^1.0.34",
|
||||
"ava": "^0.25.0",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"nano-style": "^1.0.0",
|
||||
"nyc": "^12.0.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"rebass": "^2.0.0-7",
|
||||
"rebass": "^2.0.0-6",
|
||||
"refunk": "^3.0.1",
|
||||
"rimraf": "^2.6.2",
|
||||
"styled-components": "^3.3.2",
|
||||
|
40
src/Catch.js
Normal file
40
src/Catch.js
Normal file
@ -0,0 +1,40 @@
|
||||
import React from 'react'
|
||||
|
||||
export default class Catch extends React.Component {
|
||||
static getDerivedStateFromProps (props, state) {
|
||||
if (!state.err) return null
|
||||
return { err: null }
|
||||
}
|
||||
|
||||
state = {
|
||||
err: null
|
||||
}
|
||||
|
||||
componentDidCatch (err) {
|
||||
this.setState({ err })
|
||||
}
|
||||
|
||||
render () {
|
||||
const { err } = this.state
|
||||
|
||||
if (err) {
|
||||
return (
|
||||
<pre
|
||||
children={err.toString()}
|
||||
style={{
|
||||
color: 'white',
|
||||
backgroundColor: 'red',
|
||||
fontFamily: 'Menlo, monospace',
|
||||
fontSize: '14px',
|
||||
margin: 0,
|
||||
padding: '16px',
|
||||
minHeight: '128px',
|
||||
whiteSpace: 'prewrap'
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
17
src/FileList.js
Normal file
17
src/FileList.js
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
export default ({ routes = [] }) => (
|
||||
<React.Fragment>
|
||||
<pre>{DIRNAME}</pre>
|
||||
<ul>
|
||||
{routes.map(route => (
|
||||
<li key={route.key}>
|
||||
<Link to={route.path}>
|
||||
{route.name}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</React.Fragment>
|
||||
)
|
30
src/LivePreview.js
Normal file
30
src/LivePreview.js
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
LiveProvider,
|
||||
LivePreview,
|
||||
LiveError
|
||||
} from 'react-live'
|
||||
import { ScopeConsumer } from 'react-scope-provider'
|
||||
import { Box } from 'rebass'
|
||||
|
||||
const transformCode = str => `<React.Fragment>${str}</React.Fragment>`
|
||||
|
||||
export default ({
|
||||
code,
|
||||
scope
|
||||
}) => (
|
||||
<Box mb={4}>
|
||||
<ScopeConsumer defaultScope={scope}>
|
||||
{scope => (
|
||||
<LiveProvider
|
||||
code={code}
|
||||
scope={scope}
|
||||
mountStylesheet={false}
|
||||
transformCode={transformCode}>
|
||||
<LivePreview />
|
||||
<LiveError />
|
||||
</LiveProvider>
|
||||
)}
|
||||
</ScopeConsumer>
|
||||
</Box>
|
||||
)
|
22
src/ScrollTop.js
Normal file
22
src/ScrollTop.js
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
|
||||
export default withRouter(class extends React.Component {
|
||||
componentDidUpdate (prev) {
|
||||
const { pathname, hash } = this.props.location
|
||||
if (prev.location.pathname !== pathname) {
|
||||
window.scrollTo(0, 0)
|
||||
}
|
||||
|
||||
// check performance of this
|
||||
if (hash) {
|
||||
const el = document.getElementById(hash.slice(1))
|
||||
if (!el) return
|
||||
el.scrollIntoView()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
return false
|
||||
}
|
||||
})
|
155
src/entry.js
155
src/entry.js
@ -10,44 +10,41 @@ import {
|
||||
Link,
|
||||
withRouter
|
||||
} from 'react-router-dom'
|
||||
import minimatch from 'minimatch'
|
||||
|
||||
import Catch from './Catch'
|
||||
import ScopeProvider from './ScopeProvider'
|
||||
import FileList from './FileList'
|
||||
import ScrollTop from './ScrollTop'
|
||||
|
||||
const IS_CLIENT = typeof document !== 'undefined'
|
||||
const req = require.context(DIRNAME, false, /\.(js|md|mdx|jsx)$/)
|
||||
const req = require.context(DIRNAME, true, /\.(js|md|mdx|jsx)$/)
|
||||
|
||||
const { filename, basename = '', disableScroll } = OPTIONS
|
||||
const index = filename ? path.basename(filename, path.extname(filename)) : 'index'
|
||||
|
||||
const getComponents = req => req.keys().map(key => ({
|
||||
key,
|
||||
name: path.basename(key, path.extname(key)),
|
||||
module: req(key),
|
||||
Component: req(key).default || req(key)
|
||||
}))
|
||||
req.keys().forEach(key => {
|
||||
console.log(key, minimatch(key.replace(/^\.\//, ''), MATCH) )
|
||||
})
|
||||
|
||||
const getComponents = req => req.keys()
|
||||
.filter(key => !MATCH || minimatch(key.replace(/^\.\//, ''), MATCH))
|
||||
.map(key => ({
|
||||
key,
|
||||
name: path.basename(key, path.extname(key)),
|
||||
module: req(key),
|
||||
Component: req(key).default || req(key)
|
||||
}))
|
||||
.filter(component => !/^(\.|_)/.test(component.name))
|
||||
.filter(component => typeof component.Component === 'function')
|
||||
|
||||
const initialComponents = getComponents(req)
|
||||
|
||||
const Index = ({ routes = [] }) => (
|
||||
<React.Fragment>
|
||||
<pre>{DIRNAME}</pre>
|
||||
<ul>
|
||||
{routes.map(route => (
|
||||
<li key={route.key}>
|
||||
<Link to={route.path}>
|
||||
{route.name}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
const DefaultApp = ({ render, routes }) => (
|
||||
<Switch>
|
||||
{render()}
|
||||
<Route render={props => (
|
||||
<Index
|
||||
<FileList
|
||||
{...props}
|
||||
routes={routes}
|
||||
/>
|
||||
@ -55,58 +52,8 @@ const DefaultApp = ({ render, routes }) => (
|
||||
</Switch>
|
||||
)
|
||||
|
||||
class Catch extends React.Component {
|
||||
static getDerivedStateFromProps (props, state) {
|
||||
if (!state.err) return null
|
||||
return { err: null }
|
||||
}
|
||||
|
||||
state = {
|
||||
err: null
|
||||
}
|
||||
|
||||
componentDidCatch (err) {
|
||||
this.setState({ err })
|
||||
}
|
||||
|
||||
render () {
|
||||
const { err } = this.state
|
||||
|
||||
if (err) {
|
||||
return (
|
||||
<pre
|
||||
children={err.toString()}
|
||||
style={{
|
||||
color: 'white',
|
||||
backgroundColor: 'red',
|
||||
fontFamily: 'Menlo, monospace',
|
||||
fontSize: '14px',
|
||||
margin: 0,
|
||||
padding: '16px',
|
||||
minHeight: '128px',
|
||||
whiteSpace: 'prewrap'
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
const ScrollTop = withRouter(class extends React.Component {
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.location.pathname !== prevProps.location.pathname) {
|
||||
window.scrollTo(0, 0)
|
||||
}
|
||||
}
|
||||
render () {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
const Router = IS_CLIENT ? BrowserRouter : StaticRouter
|
||||
const App = withRouter(APP ? (require(APP).default || require(APP)) : DefaultApp)
|
||||
const App = APP ? (require(APP).default || require(APP)) : DefaultApp
|
||||
|
||||
export const getRoutes = async (components = initialComponents) => {
|
||||
const routes = await components.map(async ({ key, name, module, Component }) => {
|
||||
@ -129,19 +76,29 @@ export const getRoutes = async (components = initialComponents) => {
|
||||
return Promise.all(routes)
|
||||
}
|
||||
|
||||
const RouterState = withRouter(({ render, ...props }) => {
|
||||
const route = props.routes.find(r => r.path === props.location.pathname)
|
||||
return render({ ...props, route })
|
||||
})
|
||||
|
||||
export default class Root extends React.Component {
|
||||
static defaultProps = {
|
||||
path: '/',
|
||||
basename
|
||||
}
|
||||
state = this.props
|
||||
state = {
|
||||
...this.props,
|
||||
...App.defaultProps
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
routes,
|
||||
basename,
|
||||
path = '/'
|
||||
} = this.state
|
||||
} = this.props
|
||||
|
||||
console.log('App', App.defaultProps)
|
||||
|
||||
return (
|
||||
<Router
|
||||
@ -149,28 +106,36 @@ export default class Root extends React.Component {
|
||||
basename={basename}
|
||||
location={path}>
|
||||
<React.Fragment>
|
||||
<Catch>
|
||||
<App
|
||||
routes={routes}
|
||||
render={appProps => (
|
||||
routes.map(({ Component, ...route }) => (
|
||||
<Route
|
||||
{...route}
|
||||
render={props => (
|
||||
<Catch>
|
||||
<Component
|
||||
{...props}
|
||||
{...appProps}
|
||||
{...route.props}
|
||||
<ScopeProvider>
|
||||
<Catch>
|
||||
<RouterState
|
||||
routes={routes}
|
||||
render={(router) => (
|
||||
<App
|
||||
{...router}
|
||||
routes={routes}
|
||||
render={appProps => (
|
||||
routes.map(({ Component, ...route }) => (
|
||||
<Route
|
||||
{...route}
|
||||
render={props => (
|
||||
<Catch>
|
||||
<Component
|
||||
{...props}
|
||||
{...appProps}
|
||||
{...route.props}
|
||||
/>
|
||||
</Catch>
|
||||
)}
|
||||
/>
|
||||
</Catch>
|
||||
))
|
||||
)}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
/>
|
||||
</Catch>
|
||||
{!disableScroll && <ScrollTop />}
|
||||
)}
|
||||
/>
|
||||
</Catch>
|
||||
{!disableScroll && <ScrollTop />}
|
||||
</ScopeProvider>
|
||||
</React.Fragment>
|
||||
</Router>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user