mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-14 10:04:05 +00:00
Merge pull request #2748 from ricardogobbosouza/config-pages-dir
feat: custom directories
This commit is contained in:
commit
72300d350e
@ -1,5 +1,5 @@
|
|||||||
<% if (middleware) { %>
|
<% if (middleware) { %>
|
||||||
let files = require.context('@/middleware', false, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
|
let files = require.context('@/<%= dir.middleware %>', false, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
|
||||||
let filenames = files.keys()
|
let filenames = files.keys()
|
||||||
|
|
||||||
function getModule (filename) {
|
function getModule (filename) {
|
||||||
|
@ -3,14 +3,14 @@ import Vuex from 'vuex'
|
|||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
// Recursive find files in {srcDir}/store
|
// Recursive find files in {srcDir}/{dir.store}
|
||||||
const files = require.context('@/store', true, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
|
const files = require.context('@/<%= dir.store %>', true, /^\.\/(?!<%= ignorePrefix %>).*\.(<%= extensions %>)$/)
|
||||||
const filenames = files.keys()
|
const filenames = files.keys()
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
let storeData = {}
|
let storeData = {}
|
||||||
|
|
||||||
// Check if store/index.js exists
|
// Check if {dir.store}/index.js exists
|
||||||
let indexFilename
|
let indexFilename
|
||||||
filenames.forEach((filename) => {
|
filenames.forEach((filename) => {
|
||||||
if (filename.indexOf('./index.') !== -1) {
|
if (filename.indexOf('./index.') !== -1) {
|
||||||
@ -57,10 +57,10 @@ function getModule (filename) {
|
|||||||
const file = files(filename)
|
const file = files(filename)
|
||||||
const module = file.default || file
|
const module = file.default || file
|
||||||
if (module.commit) {
|
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') {
|
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
|
return module
|
||||||
}
|
}
|
||||||
|
@ -137,15 +137,15 @@ module.exports = class Builder {
|
|||||||
// Check if pages dir exists and warn if not
|
// Check if pages dir exists and warn if not
|
||||||
this._nuxtPages = typeof this.options.build.createRoutes !== 'function'
|
this._nuxtPages = typeof this.options.build.createRoutes !== 'function'
|
||||||
if (this._nuxtPages) {
|
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
|
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(
|
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 {
|
} else {
|
||||||
throw new Error(
|
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,
|
router: this.options.router,
|
||||||
env: this.options.env,
|
env: this.options.env,
|
||||||
head: this.options.head,
|
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,
|
store: this.options.store,
|
||||||
css: this.options.css,
|
css: this.options.css,
|
||||||
plugins: this.plugins,
|
plugins: this.plugins,
|
||||||
@ -263,6 +263,7 @@ module.exports = class Builder {
|
|||||||
: this.options.loading,
|
: this.options.loading,
|
||||||
transition: this.options.transition,
|
transition: this.options.transition,
|
||||||
layoutTransition: this.options.layoutTransition,
|
layoutTransition: this.options.layoutTransition,
|
||||||
|
dir: this.options.dir,
|
||||||
components: {
|
components: {
|
||||||
ErrorPage: this.options.ErrorPage
|
ErrorPage: this.options.ErrorPage
|
||||||
? this.relativeToBuild(this.options.ErrorPage)
|
? this.relativeToBuild(this.options.ErrorPage)
|
||||||
@ -271,8 +272,8 @@ module.exports = class Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -- Layouts --
|
// -- Layouts --
|
||||||
if (existsSync(resolve(this.options.srcDir, 'layouts'))) {
|
if (existsSync(resolve(this.options.srcDir, this.options.dir.layouts))) {
|
||||||
const layoutsFiles = await glob('layouts/**/*.{vue,js}', {
|
const layoutsFiles = await glob(`${this.options.dir.layouts}/**/*.{vue,js}`, {
|
||||||
cwd: this.options.srcDir,
|
cwd: this.options.srcDir,
|
||||||
ignore: this.options.ignore
|
ignore: this.options.ignore
|
||||||
})
|
})
|
||||||
@ -297,7 +298,7 @@ module.exports = class Builder {
|
|||||||
if (!templateVars.components.ErrorPage && hasErrorLayout) {
|
if (!templateVars.components.ErrorPage && hasErrorLayout) {
|
||||||
templateVars.components.ErrorPage = this.relativeToBuild(
|
templateVars.components.ErrorPage = this.relativeToBuild(
|
||||||
this.options.srcDir,
|
this.options.srcDir,
|
||||||
'layouts/error.vue'
|
`${this.options.dir.layouts}/error.vue`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,7 +315,7 @@ module.exports = class Builder {
|
|||||||
if (this._nuxtPages) {
|
if (this._nuxtPages) {
|
||||||
// Use nuxt.js createRoutes bases on pages/
|
// Use nuxt.js createRoutes bases on pages/
|
||||||
const files = {}
|
const files = {}
|
||||||
;(await glob('pages/**/*.{vue,js}', {
|
;(await glob(`${this.options.dir.pages}/**/*.{vue,js}`, {
|
||||||
cwd: this.options.srcDir,
|
cwd: this.options.srcDir,
|
||||||
ignore: this.options.ignore
|
ignore: this.options.ignore
|
||||||
})).forEach(f => {
|
})).forEach(f => {
|
||||||
@ -325,7 +326,8 @@ module.exports = class Builder {
|
|||||||
})
|
})
|
||||||
templateVars.router.routes = createRoutes(
|
templateVars.router.routes = createRoutes(
|
||||||
Object.values(files),
|
Object.values(files),
|
||||||
this.options.srcDir
|
this.options.srcDir,
|
||||||
|
this.options.dir.pages
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
templateVars.router.routes = this.options.build.createRoutes(
|
templateVars.router.routes = this.options.build.createRoutes(
|
||||||
@ -638,17 +640,17 @@ module.exports = class Builder {
|
|||||||
watchFiles() {
|
watchFiles() {
|
||||||
const src = this.options.srcDir
|
const src = this.options.srcDir
|
||||||
let patterns = [
|
let patterns = [
|
||||||
r(src, 'layouts'),
|
r(src, this.options.dir.layouts),
|
||||||
r(src, 'store'),
|
r(src, this.options.dir.store),
|
||||||
r(src, 'middleware'),
|
r(src, this.options.dir.middleware),
|
||||||
r(src, 'layouts/*.{vue,js}'),
|
r(src, `${this.options.dir.layouts}/*.{vue,js}`),
|
||||||
r(src, 'layouts/**/*.{vue,js}')
|
r(src, `${this.options.dir.layouts}/**/*.{vue,js}`)
|
||||||
]
|
]
|
||||||
if (this._nuxtPages) {
|
if (this._nuxtPages) {
|
||||||
patterns.push(
|
patterns.push(
|
||||||
r(src, 'pages'),
|
r(src, this.options.dir.pages),
|
||||||
r(src, 'pages/*.{vue,js}'),
|
r(src, `${this.options.dir.pages}/*.{vue,js}`),
|
||||||
r(src, 'pages/**/*.{vue,js}')
|
r(src, `${this.options.dir.pages}/**/*.{vue,js}`)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
patterns = _.map(patterns, p => upath.normalizeSafe(p))
|
patterns = _.map(patterns, p => upath.normalizeSafe(p))
|
||||||
|
@ -27,7 +27,7 @@ module.exports = class Generator {
|
|||||||
this.builder = builder
|
this.builder = builder
|
||||||
|
|
||||||
// Set variables
|
// 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.srcBuiltPath = resolve(this.options.buildDir, 'dist')
|
||||||
this.distPath = resolve(this.options.rootDir, this.options.generate.dir)
|
this.distPath = resolve(this.options.rootDir, this.options.generate.dir)
|
||||||
this.distNuxtPath = join(
|
this.distNuxtPath = join(
|
||||||
|
@ -19,6 +19,13 @@ module.exports = function webpackBaseConfig({ name, isServer }) {
|
|||||||
// Prioritize nested node_modules in webpack search path (#2558)
|
// Prioritize nested node_modules in webpack search path (#2558)
|
||||||
const webpackModulesDir = ['node_modules'].concat(this.options.modulesDir)
|
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 = {
|
const config = {
|
||||||
name,
|
name,
|
||||||
entry: {
|
entry: {
|
||||||
@ -39,16 +46,12 @@ module.exports = function webpackBaseConfig({ name, isServer }) {
|
|||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.js', '.json', '.vue', '.jsx'],
|
extensions: ['.js', '.json', '.vue', '.jsx'],
|
||||||
alias: {
|
alias: Object.assign({
|
||||||
'~': join(this.options.srcDir),
|
'~': join(this.options.srcDir),
|
||||||
'~~': join(this.options.rootDir),
|
'~~': join(this.options.rootDir),
|
||||||
'@': join(this.options.srcDir),
|
'@': join(this.options.srcDir),
|
||||||
'@@': join(this.options.rootDir),
|
'@@': join(this.options.rootDir)
|
||||||
// Used by vue-loader so we can use in templates
|
}, configAlias),
|
||||||
// with <img src="~/assets/nuxt.png"/>
|
|
||||||
assets: join(this.options.srcDir, 'assets'),
|
|
||||||
static: join(this.options.srcDir, 'static')
|
|
||||||
},
|
|
||||||
modules: webpackModulesDir
|
modules: webpackModulesDir
|
||||||
},
|
},
|
||||||
resolveLoader: {
|
resolveLoader: {
|
||||||
|
@ -56,16 +56,17 @@ module.exports = function styleLoader(ext, loaders = [], isVueLoader = false) {
|
|||||||
|
|
||||||
// css-loader
|
// css-loader
|
||||||
// https://github.com/webpack-contrib/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({
|
loaders.unshift({
|
||||||
loader: 'css-loader',
|
loader: 'css-loader',
|
||||||
options: {
|
options: {
|
||||||
sourceMap,
|
sourceMap,
|
||||||
minimize: !this.options.dev,
|
minimize: !this.options.dev,
|
||||||
importLoaders: loaders.length, // Important!
|
importLoaders: loaders.length, // Important!
|
||||||
alias: {
|
alias: cssLoaderAlias
|
||||||
'/static': join(this.options.srcDir, 'static'),
|
|
||||||
'/assets': join(this.options.srcDir, 'assets')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ Options.from = function (_options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If store defined, update store options to true unless explicitly disabled
|
// 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
|
options.store = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +284,14 @@ Options.defaults = {
|
|||||||
name: 'layout',
|
name: 'layout',
|
||||||
mode: 'out-in'
|
mode: 'out-in'
|
||||||
},
|
},
|
||||||
|
dir: {
|
||||||
|
assets: 'assets',
|
||||||
|
layouts: 'layouts',
|
||||||
|
middleware: 'middleware',
|
||||||
|
pages: 'pages',
|
||||||
|
static: 'static',
|
||||||
|
store: 'store'
|
||||||
|
},
|
||||||
router: {
|
router: {
|
||||||
mode: 'history',
|
mode: 'history',
|
||||||
base: '/',
|
base: '/',
|
||||||
|
@ -254,11 +254,11 @@ function cleanChildrenRoutes(routes, isChild = false) {
|
|||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createRoutes = function createRoutes(files, srcDir) {
|
exports.createRoutes = function createRoutes(files, srcDir, pagesDir) {
|
||||||
let routes = []
|
let routes = []
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
let keys = file
|
let keys = file
|
||||||
.replace(/^pages/, '')
|
.replace(RegExp(`^${pagesDir}`), '')
|
||||||
.replace(/\.(vue|js)$/, '')
|
.replace(/\.(vue|js)$/, '')
|
||||||
.replace(/\/{2,}/g, '/')
|
.replace(/\/{2,}/g, '/')
|
||||||
.split('/')
|
.split('/')
|
||||||
|
@ -250,7 +250,7 @@ module.exports = class Renderer {
|
|||||||
|
|
||||||
// For serving static/ files to /
|
// For serving static/ files to /
|
||||||
const staticMiddleware = serveStatic(
|
const staticMiddleware = serveStatic(
|
||||||
resolve(this.options.srcDir, 'static'),
|
resolve(this.options.srcDir, this.options.dir.static),
|
||||||
this.options.render.static
|
this.options.render.static
|
||||||
)
|
)
|
||||||
staticMiddleware.prefix = this.options.render.static.prefix
|
staticMiddleware.prefix = this.options.render.static.prefix
|
||||||
|
62
test/custom-dirs.test.js
Normal file
62
test/custom-dirs.test.js
Normal 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()
|
||||||
|
})
|
3
test/fixtures/custom-dirs/custom-assets/app.css
vendored
Normal file
3
test/fixtures/custom-dirs/custom-assets/app.css
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.global-css-selector {
|
||||||
|
color: red;
|
||||||
|
}
|
6
test/fixtures/custom-dirs/custom-layouts/default.vue
vendored
Normal file
6
test/fixtures/custom-dirs/custom-layouts/default.vue
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<nuxt/>
|
||||||
|
<p>I have custom layouts directory</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
3
test/fixtures/custom-dirs/custom-middleware/user-agent.js
vendored
Normal file
3
test/fixtures/custom-dirs/custom-middleware/user-agent.js
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default function (context) {
|
||||||
|
context.userAgent = process.server ? context.req.headers['user-agent'] : navigator.userAgent
|
||||||
|
}
|
5
test/fixtures/custom-dirs/custom-pages/index.vue
vendored
Normal file
5
test/fixtures/custom-dirs/custom-pages/index.vue
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>I have custom pages directory</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
12
test/fixtures/custom-dirs/custom-pages/user-agent.vue
vendored
Normal file
12
test/fixtures/custom-dirs/custom-pages/user-agent.vue
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<pre>{{ userAgent }}</pre>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
middleware: 'user-agent',
|
||||||
|
asyncData({ userAgent }) {
|
||||||
|
return { userAgent }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
1
test/fixtures/custom-dirs/custom-static/test.txt
vendored
Normal file
1
test/fixtures/custom-dirs/custom-static/test.txt
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
A test here :)
|
17
test/fixtures/custom-dirs/custom-store/index.js
vendored
Normal file
17
test/fixtures/custom-dirs/custom-store/index.js
vendored
Normal 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
|
11
test/fixtures/custom-dirs/nuxt.config.js
vendored
Normal file
11
test/fixtures/custom-dirs/nuxt.config.js
vendored
Normal 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'
|
||||||
|
}
|
||||||
|
}
|
5
test/fixtures/custom-dirs/package.json
vendored
Normal file
5
test/fixtures/custom-dirs/package.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "custom-dirs",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user