Skip to content
This repository has been archived by the owner on Jan 14, 2020. It is now read-only.

File system routes #96

Merged
merged 26 commits into from
Aug 15, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6eb544f
fs-routes working prototype
IlyaSemenov Jun 3, 2018
15e3388
Make entry file optional
IlyaSemenov Jun 3, 2018
572a77b
Allow to override `pages` path
IlyaSemenov Jun 3, 2018
27f2f1f
Recursive and dynamic fs-routes
IlyaSemenov Jun 3, 2018
b08c623
Fix formatting
IlyaSemenov Jun 3, 2018
164f271
Simplified fs-routes example
IlyaSemenov Jun 5, 2018
eda1862
Merge entry routes with fs routes
IlyaSemenov Jun 5, 2018
bd4ee22
Refactor fs-routes files collector
IlyaSemenov Jun 5, 2018
5688c70
Use require.context for hot reload of ream entry
IlyaSemenov Jun 5, 2018
b6e518c
Add tests for fs-routes
IlyaSemenov Jun 5, 2018
f60440e
Simplify fs-routes test runner
IlyaSemenov Jun 5, 2018
a83e367
Update fs-routes expected routes naming and format
IlyaSemenov Jun 6, 2018
222315d
Watch pages path and recreate routes
IlyaSemenov Jun 6, 2018
92a3227
Remove stale debug code
IlyaSemenov Jun 6, 2018
ca88c69
tweaks
IlyaSemenov Jun 6, 2018
0d46331
Handle non-existent pages directory
IlyaSemenov Jun 6, 2018
11737bc
Decouple write-routes.js from ream API.
IlyaSemenov Jun 6, 2018
56b846a
Show explanatory message when / gives 404 in dev mode
IlyaSemenov Jun 6, 2018
7c81524
Change parameters naming
IlyaSemenov Jun 6, 2018
2e3357d
adjust naming
IlyaSemenov Jun 7, 2018
3911d61
fs.pathExists instead of fs.exists
egoist Jun 14, 2018
94943b8
Fix create-app-template when api.options.entry is disabled
IlyaSemenov Jun 16, 2018
2b354a0
Hot reload for added entry file with chokidar
IlyaSemenov Jun 23, 2018
bab626b
Rollback this.resolveOutDir change
IlyaSemenov Jun 23, 2018
18ba879
Merge branch 'master' into features/fs-routes
egoist Aug 7, 2018
74ef009
tweaks
egoist Aug 15, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 32 additions & 15 deletions app/create-app-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ const pathToId = file => {
}

module.exports = api => {
let entryExists = false
if (api.options.entry) {
try {
require.resolve(api.resolveBaseDir(api.options.entry))
entryExists = true
} catch (err) {}
}
Copy link
Collaborator

@egoist egoist Jun 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use require.context in the runtime code to check if there's entry file so that the app will be properly reloaded when you add an entry file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added hot reload for the entry with require.context.

Since it requires string literals at compile time, the code is a bit verbose. I guess it could be moved into a reusable code generation function eventually (so the code becomes something like const _entry = ${importOptional(api.baseDir, api.options.entry)}, but I'm not entirely sure where you'd like it to reside.


const enhanceAppFiles = [...api.enhanceAppFiles].map((filepath, index) => ({
id: `${pathToId(filepath)}_${index}`,
filepath: slash(filepath)
Expand All @@ -25,8 +33,6 @@ module.exports = api => {
import Vue from 'vue'
import Meta from 'vue-meta'
import Router from 'vue-router'
// eslint-disable-next-line import/no-unresolved
import _entry from '#app-entry'
import { getRequireDefault } from '#app/utils'

Vue.config.productionTip = false
Expand All @@ -40,6 +46,10 @@ module.exports = api => {
tagIDKeyName: 'rhid'
})

const _entry = ${
entryExists ? `getRequireDefault(require('#app-entry'))` : `{}`
}

const enhanceApp = getRequireDefault(require('#app/enhance-app'))

${[...enhanceAppFiles]
Expand All @@ -52,19 +62,13 @@ module.exports = api => {

export default context => {
const entry = typeof _entry === 'function' ? _entry() : _entry
let { router, extendRootOptions } = entry
if (context) {
context.entry = entry
}

if (__DEV__) {
if (!router) {
throw new Error('You must export "router" in entry file!')
}
}

const rootOptions = {
_isReamRoot: true
_isReamRoot: true,
router: entry.router
}
const getInitialDataContextFns = [
entry.getInitialDataContext
Expand All @@ -75,7 +79,6 @@ module.exports = api => {

const event = new Vue()
const enhanceContext = {
router,
rootOptions,
entry,
ssrContext: context,
Expand All @@ -90,10 +93,6 @@ module.exports = api => {

enhanceApp(enhanceContext, context)

if (extendRootOptions) {
extendRootOptions(rootOptions)
}

${[...enhanceAppFiles]
.map(file =>
`
Expand All @@ -104,6 +103,24 @@ module.exports = api => {
)
.join('\n')}

if (entry.extendRootOptions) {
entry.extendRootOptions(rootOptions)
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why after enhanceAppFiles 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that user provided entry callback should be called last (to have chance to correct all previous plugin-imposed setup). This change is not needed for the PR. If you dislike it I can revert.

const { router } = rootOptions

if (__DEV__) {
if (!router) {
throw new Error(${JSON.stringify(
`You must ${
api.options.fsRoutes
? `create ${api.options.fsRoutes.path}/*.vue or `
: ``
}export "router" in ${api.options.entry || `entry file`}!`
)})
}
}
Copy link
Collaborator

@egoist egoist Jun 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about creating a default router instance here if entry.router is not provided, and use router.addRoutes to add routes in the fs-routes plugin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reworked it the way you described. One of the benefits it's that it's now possible to merge entry routes with fs routes (with former taking precedence).

However, it's now not possible to detect a misconfigured router in create-app-template.js, as router doesn't expose the full set of routes (you can't rely on router.options.routes as it only contains the initial set of routes and not the ones added by addRoutes):
https://github.com/vuejs/vue-router/blob/4cb6699ecc4453a992429b28f32f4add3e2680b6/src/create-matcher.js#L171

Thoughts?


const app = new Vue(rootOptions)

return {
Expand Down
3 changes: 1 addition & 2 deletions app/enhance-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,9 @@ const Error = {
}

export default ({ rootOptions, entry }, context) => {
const { router, root = Root, error = Error } = entry
const { root = Root, error = Error } = entry

const App = {
router,
dataStore: createDataStore(),
data() {
return {
Expand Down
24 changes: 24 additions & 0 deletions examples/fs-routes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Auto generates routes

## How to use

```bash
git clone https://github.com/ream/ream.git
```

Install dependencies:

```bash
cd examples/fs-routes
yarn
```

Run it:

```bash
# Start development server
yarn dev

# Start production server
yarn build && yarn start
```
12 changes: 12 additions & 0 deletions examples/fs-routes/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"private": true,
"name": "fs-routes",
"scripts": {
"dev": "ream dev",
"start": "ream start",
"build": "ream build"
},
"dependencies": {
"ream": "latest"
}
}
3 changes: 3 additions & 0 deletions examples/fs-routes/pages/about.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<div>fs-routes example app.</div>
</template>
6 changes: 6 additions & 0 deletions examples/fs-routes/pages/catalog/_item_id.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<div>
<h1>Item: {{ $route.params.item_id }}</h1>
<router-view />
</div>
</template>
3 changes: 3 additions & 0 deletions examples/fs-routes/pages/catalog/_item_id/images.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<div>Images</div>
</template>
3 changes: 3 additions & 0 deletions examples/fs-routes/pages/catalog/_item_id/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<div>Info</div>
</template>
20 changes: 20 additions & 0 deletions examples/fs-routes/pages/catalog/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<ul>
<li><router-link to="/catalog/specials">Specials</router-link></li>
<li v-for="item in items" :key="item">
Item: {{ item }}
<router-link :to="`/catalog/${item}`">Info</router-link>
<router-link :to="`/catalog/${item}/images`">Images</router-link>
</li>
</ul>
</template>

<script>
export default {
data () {
return {
items: [10, 20, 30]
}
}
}
</script>
3 changes: 3 additions & 0 deletions examples/fs-routes/pages/catalog/specials.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<div>Special offers!</div>
</template>
7 changes: 7 additions & 0 deletions examples/fs-routes/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<template>
<ul>
<li><router-link to="/about">About</router-link></li>
<li><router-link to="/catalog">Catalog</router-link></li>
<li><router-link to="/user">User</router-link></li>
</ul>
</template>
9 changes: 9 additions & 0 deletions examples/fs-routes/pages/user.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<template>
<div>
<h1>User</h1>
<router-link to="/user">Profile</router-link>
<router-link to="/user/friends">Friends</router-link>
<hr>
<router-view />
</div>
</template>
3 changes: 3 additions & 0 deletions examples/fs-routes/pages/user/friends.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<div>Friends</div>
</template>
3 changes: 3 additions & 0 deletions examples/fs-routes/pages/user/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<div>Profile</div>
</template>
7 changes: 6 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class Ream extends Event {
this.options = merge(
{
entry: 'index.js',
fsRoutes: {
path: 'pages'
},
plugins: [],
server: {
host: process.env.HOST || '0.0.0.0',
Expand Down Expand Up @@ -118,7 +121,8 @@ class Ream extends Event {
basePlugin,
require('./plugins/vuex'),
require('./plugins/apollo'),
require('./plugins/pwa')
require('./plugins/pwa'),
require('./plugins/fs-routes')
]
.concat(this.options.plugins)
.filter(Boolean)
Expand Down Expand Up @@ -234,6 +238,7 @@ class Ream extends Event {
createAppFile,
require('../app/create-app-template')(this)
)
await this.hooks.run('onPrepareFiles')
}

async getServer() {
Expand Down
21 changes: 21 additions & 0 deletions lib/plugins/fs-routes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const path = require('path')
const fs = require('fs-extra')

module.exports = {
name: 'builtin:fs-routes',
apply(api) {
if (
api.options.fsRoutes &&
fs.existsSync(api.resolveBaseDir(api.options.fsRoutes.path))
) {
api.enhanceAppFiles.add(path.join(__dirname, 'inject.js'))

api.hooks.add('onPrepareFiles', async () => {
await fs.writeFile(
api.resolveOutDir('router.js'),
await require('./router-template')(api, api.options.fsRoutes.path)
)
})
}
}
}
7 changes: 7 additions & 0 deletions lib/plugins/fs-routes/inject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import createRouter from '#out/router' // eslint-disable-line import/no-unresolved

export default ({ rootOptions, ssrContext }) => {
if (rootOptions.router) return

rootOptions.router = createRouter(ssrContext)
}
Loading