Merge pull request #2748 from ricardogobbosouza/config-pages-dir

feat: custom directories
This commit is contained in:
Sébastien Chopin 2018-02-06 13:21:32 +01:00 committed by GitHub
commit 72300d350e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 179 additions and 40 deletions

View File

@ -1,5 +1,5 @@
<% if (middleware) { %>
let files = require.context('@/middleware', false, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
let files = require.context('@/<%= dir.middleware %>', false, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
let filenames = files.keys()
function getModule (filename) {

View File

@ -3,14 +3,14 @@ import Vuex from 'vuex'
Vue.use(Vuex)
// Recursive find files in {srcDir}/store
const files = require.context('@/store', true, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
// Recursive find files in {srcDir}/{dir.store}
const files = require.context('@/<%= dir.store %>', true, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
const filenames = files.keys()
// Store
let storeData = {}
// Check if store/index.js exists
// Check if {dir.store}/index.js exists
let indexFilename
filenames.forEach((filename) => {
if (filename.indexOf('./index.') !== -1) {
@ -57,10 +57,10 @@ function getModule (filename) {
const file = files(filename)
const module = file.default || file
if (module.commit) {
throw new Error('[nuxt] store/' + filename.replace('./', '') + ' should export a method which returns a Vuex instance.')
throw new Error('[nuxt] <%= dir.store %>/' + filename.replace('./', '') + ' should export a method which returns a Vuex instance.')
}
if (module.state && typeof module.state !== 'function') {
throw new Error('[nuxt] state should be a function in store/' + filename.replace('./', ''))
throw new Error('[nuxt] state should be a function in <%= dir.store %>/' + filename.replace('./', ''))
}
return module
}

View File

@ -137,15 +137,15 @@ module.exports = class Builder {
// Check if pages dir exists and warn if not
this._nuxtPages = typeof this.options.build.createRoutes !== 'function'
if (this._nuxtPages) {
if (!existsSync(join(this.options.srcDir, 'pages'))) {
if (!existsSync(join(this.options.srcDir, this.options.dir.pages))) {
let dir = this.options.srcDir
if (existsSync(join(this.options.srcDir, '..', 'pages'))) {
if (existsSync(join(this.options.srcDir, '..', this.options.dir.pages))) {
throw new Error(
`No \`pages\` directory found in ${dir}. Did you mean to run \`nuxt\` in the parent (\`../\`) directory?`
`No \`${this.options.dir.pages}\` directory found in ${dir}. Did you mean to run \`nuxt\` in the parent (\`../\`) directory?`
)
} else {
throw new Error(
`Couldn't find a \`pages\` directory in ${dir}. Please create one under the project root`
`Couldn't find a \`${this.options.dir.pages}\` directory in ${dir}. Please create one under the project root`
)
}
}
@ -250,7 +250,7 @@ module.exports = class Builder {
router: this.options.router,
env: this.options.env,
head: this.options.head,
middleware: existsSync(join(this.options.srcDir, 'middleware')),
middleware: existsSync(join(this.options.srcDir, this.options.dir.middleware)),
store: this.options.store,
css: this.options.css,
plugins: this.plugins,
@ -263,6 +263,7 @@ module.exports = class Builder {
: this.options.loading,
transition: this.options.transition,
layoutTransition: this.options.layoutTransition,
dir: this.options.dir,
components: {
ErrorPage: this.options.ErrorPage
? this.relativeToBuild(this.options.ErrorPage)
@ -271,8 +272,8 @@ module.exports = class Builder {
}
// -- Layouts --
if (existsSync(resolve(this.options.srcDir, 'layouts'))) {
const layoutsFiles = await glob('layouts/**/*.{vue,js}', {
if (existsSync(resolve(this.options.srcDir, this.options.dir.layouts))) {
const layoutsFiles = await glob(`${this.options.dir.layouts}/**/*.{vue,js}`, {
cwd: this.options.srcDir,
ignore: this.options.ignore
})
@ -297,7 +298,7 @@ module.exports = class Builder {
if (!templateVars.components.ErrorPage && hasErrorLayout) {
templateVars.components.ErrorPage = this.relativeToBuild(
this.options.srcDir,
'layouts/error.vue'
`${this.options.dir.layouts}/error.vue`
)
}
}
@ -314,7 +315,7 @@ module.exports = class Builder {
if (this._nuxtPages) {
// Use nuxt.js createRoutes bases on pages/
const files = {}
;(await glob('pages/**/*.{vue,js}', {
;(await glob(`${this.options.dir.pages}/**/*.{vue,js}`, {
cwd: this.options.srcDir,
ignore: this.options.ignore
})).forEach(f => {
@ -325,7 +326,8 @@ module.exports = class Builder {
})
templateVars.router.routes = createRoutes(
Object.values(files),
this.options.srcDir
this.options.srcDir,
this.options.dir.pages
)
} else {
templateVars.router.routes = this.options.build.createRoutes(
@ -638,17 +640,17 @@ module.exports = class Builder {
watchFiles() {
const src = this.options.srcDir
let patterns = [
r(src, 'layouts'),
r(src, 'store'),
r(src, 'middleware'),
r(src, 'layouts/*.{vue,js}'),
r(src, 'layouts/**/*.{vue,js}')
r(src, this.options.dir.layouts),
r(src, this.options.dir.store),
r(src, this.options.dir.middleware),
r(src, `${this.options.dir.layouts}/*.{vue,js}`),
r(src, `${this.options.dir.layouts}/**/*.{vue,js}`)
]
if (this._nuxtPages) {
patterns.push(
r(src, 'pages'),
r(src, 'pages/*.{vue,js}'),
r(src, 'pages/**/*.{vue,js}')
r(src, this.options.dir.pages),
r(src, `${this.options.dir.pages}/*.{vue,js}`),
r(src, `${this.options.dir.pages}/**/*.{vue,js}`)
)
}
patterns = _.map(patterns, p => upath.normalizeSafe(p))

View File

@ -27,7 +27,7 @@ module.exports = class Generator {
this.builder = builder
// Set variables
this.staticRoutes = resolve(this.options.srcDir, 'static')
this.staticRoutes = resolve(this.options.srcDir, this.options.dir.static)
this.srcBuiltPath = resolve(this.options.buildDir, 'dist')
this.distPath = resolve(this.options.rootDir, this.options.generate.dir)
this.distNuxtPath = join(

View File

@ -19,6 +19,13 @@ module.exports = function webpackBaseConfig({ name, isServer }) {
// Prioritize nested node_modules in webpack search path (#2558)
const webpackModulesDir = ['node_modules'].concat(this.options.modulesDir)
const configAlias = {}
// Used by vue-loader so we can use in templates
// with <img src="~/assets/nuxt.png"/>
configAlias[this.options.dir.assets] = join(this.options.srcDir, this.options.dir.assets)
configAlias[this.options.dir.static] = join(this.options.srcDir, this.options.dir.static)
const config = {
name,
entry: {
@ -39,16 +46,12 @@ module.exports = function webpackBaseConfig({ name, isServer }) {
},
resolve: {
extensions: ['.js', '.json', '.vue', '.jsx'],
alias: {
alias: Object.assign({
'~': join(this.options.srcDir),
'~~': join(this.options.rootDir),
'@': join(this.options.srcDir),
'@@': join(this.options.rootDir),
// Used by vue-loader so we can use in templates
// with <img src="~/assets/nuxt.png"/>
assets: join(this.options.srcDir, 'assets'),
static: join(this.options.srcDir, 'static')
},
'@@': join(this.options.rootDir)
}, configAlias),
modules: webpackModulesDir
},
resolveLoader: {

View File

@ -56,16 +56,17 @@ module.exports = function styleLoader(ext, loaders = [], isVueLoader = false) {
// css-loader
// https://github.com/webpack-contrib/css-loader
const cssLoaderAlias = {}
cssLoaderAlias[`/${this.options.dir.assets}`] = join(this.options.srcDir, this.options.dir.assets)
cssLoaderAlias[`/${this.options.dir.static}`] = join(this.options.srcDir, this.options.dir.static)
loaders.unshift({
loader: 'css-loader',
options: {
sourceMap,
minimize: !this.options.dev,
importLoaders: loaders.length, // Important!
alias: {
'/static': join(this.options.srcDir, 'static'),
'/assets': join(this.options.srcDir, 'assets')
}
alias: cssLoaderAlias
}
})

View File

@ -85,7 +85,7 @@ Options.from = function (_options) {
}
// If store defined, update store options to true unless explicitly disabled
if (options.store !== false && existsSync(join(options.srcDir, 'store'))) {
if (options.store !== false && existsSync(join(options.srcDir, options.dir.store))) {
options.store = true
}
@ -284,6 +284,14 @@ Options.defaults = {
name: 'layout',
mode: 'out-in'
},
dir: {
assets: 'assets',
layouts: 'layouts',
middleware: 'middleware',
pages: 'pages',
static: 'static',
store: 'store'
},
router: {
mode: 'history',
base: '/',

View File

@ -254,11 +254,11 @@ function cleanChildrenRoutes(routes, isChild = false) {
return routes
}
exports.createRoutes = function createRoutes(files, srcDir) {
exports.createRoutes = function createRoutes(files, srcDir, pagesDir) {
let routes = []
files.forEach(file => {
let keys = file
.replace(/^pages/, '')
.replace(RegExp(`^${pagesDir}`), '')
.replace(/\.(vue|js)$/, '')
.replace(/\/{2,}/g, '/')
.split('/')

View File

@ -250,7 +250,7 @@ module.exports = class Renderer {
// For serving static/ files to /
const staticMiddleware = serveStatic(
resolve(this.options.srcDir, 'static'),
resolve(this.options.srcDir, this.options.dir.static),
this.options.render.static
)
staticMiddleware.prefix = this.options.render.static.prefix

62
test/custom-dirs.test.js Normal file
View File

@ -0,0 +1,62 @@
import test from 'ava'
import { resolve } from 'path'
import rp from 'request-promise-native'
import { Nuxt, Builder } from '..'
import { interceptLog } from './helpers/console'
const port = 4007
const url = route => 'http://localhost:' + port + route
let nuxt = null
let builder = null
// Init nuxt.js and create server listening on localhost:4000
test.before('Init Nuxt.js', async t => {
const rootDir = resolve(__dirname, 'fixtures/custom-dirs')
let config = require(resolve(rootDir, 'nuxt.config.js'))
config.rootDir = rootDir
config.dev = false
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(config)
builder = new Builder(nuxt)
await builder.build()
await nuxt.listen(4007, 'localhost')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
})
test('custom assets directory', async t => {
const { html } = await nuxt.renderRoute('/')
t.true(html.includes('.global-css-selector'))
})
test('custom layouts directory', async t => {
const { html } = await nuxt.renderRoute('/')
t.true(html.includes('<p>I have custom layouts directory</p>'))
})
test('custom middleware directory', async t => {
const window = await nuxt.renderAndGetWindow(url('/user-agent'))
const html = window.document.body.innerHTML
t.true(html.includes('<pre>Mozilla'))
})
test('custom pages directory', async t => {
const { html } = await nuxt.renderRoute('/')
t.true(html.includes('<h1>I have custom pages directory</h1>'))
})
test('custom static directory', async t => {
const { headers } = await rp(url('/test.txt'), {
resolveWithFullResponse: true
})
t.is(headers['cache-control'], 'public, max-age=0')
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -0,0 +1,3 @@
.global-css-selector {
color: red;
}

View File

@ -0,0 +1,6 @@
<template>
<div>
<nuxt/>
<p>I have custom layouts directory</p>
</div>
</template>

View File

@ -0,0 +1,3 @@
export default function (context) {
context.userAgent = process.server ? context.req.headers['user-agent'] : navigator.userAgent
}

View File

@ -0,0 +1,5 @@
<template>
<div>
<h1>I have custom pages directory</h1>
</div>
</template>

View File

@ -0,0 +1,12 @@
<template>
<pre>{{ userAgent }}</pre>
</template>
<script>
export default {
middleware: 'user-agent',
asyncData({ userAgent }) {
return { userAgent }
}
}
</script>

View File

@ -0,0 +1 @@
A test here :)

View File

@ -0,0 +1,17 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = () => new Vuex.Store({
state: {
counter: 0
},
mutations: {
increment(state) {
state.counter++
}
}
})
export default store

View File

@ -0,0 +1,11 @@
module.exports = {
css: [{ src: '~/custom-assets/app.css' }],
dir: {
assets: 'custom-assets',
layouts: 'custom-layouts',
middleware: 'custom-middleware',
pages: 'custom-pages',
static: 'custom-static',
store: 'custom-store'
}
}

View File

@ -0,0 +1,5 @@
{
"name": "custom-dirs",
"version": "1.0.0",
"dependencies": {}
}