Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: .nuxtignore #4647

Merged
merged 35 commits into from Jan 29, 2019
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6c7b938
feat: .nuxtignore in pages and layouts
clarkdo Dec 28, 2018
04a1c96
refactor: make code clear
clarkdo Dec 28, 2018
77b00bc
fix: test
clarkdo Dec 28, 2018
5660631
fix(test): move test into correct suite
clarkdo Dec 28, 2018
ced2929
refactor: extract resolveFiles
clarkdo Dec 28, 2018
c5eb385
feat: ignore stores
clarkdo Dec 31, 2018
bf6c63c
Merge remote-tracking branch 'origin/dev' into feat/nuxtignore
clarkdo Dec 31, 2018
f97b388
fix: lint
clarkdo Dec 31, 2018
a52d4cc
feat: ignore middleware
clarkdo Dec 31, 2018
08cb300
Merge branch 'dev' into feat/nuxtignore
clarkdo Dec 31, 2018
20ba116
Merge branch 'dev' into feat/nuxtignore
clarkdo Jan 2, 2019
fcf9258
feat: watch ignore file
clarkdo Dec 31, 2018
bd82c20
refactor: move ignore.js to utils
clarkdo Jan 2, 2019
f6dc3d0
Merge branch 'dev' into feat/nuxtignore
pi0 Jan 6, 2019
fbe7081
refactor: simplify forEach
clarkdo Jan 7, 2019
a16dd6e
refactor: move ignore back to builder
clarkdo Jan 7, 2019
b03e3a5
refactor: better name for storeModules
clarkdo Jan 7, 2019
445b49c
fix: syntax error
clarkdo Jan 7, 2019
24b0c2a
Merge branch 'dev' into feat/nuxtignore
clarkdo Jan 7, 2019
33fd252
refactor: use array.map in resolveRelative
clarkdo Jan 7, 2019
2e68f4a
Merge remote-tracking branch 'origin/dev' into feat/nuxtignore
clarkdo Jan 7, 2019
bd96c01
Merge branch 'dev' of github.com:nuxt/nuxt.js into feat/nuxtignore
clarkdo Jan 7, 2019
7e52f3b
Merge remote-tracking branch 'upstream/dev' into feat/nuxtignore
clarkdo Jan 14, 2019
831482f
fix: missing configLayouts
clarkdo Jan 14, 2019
f932271
Merge branch 'dev' into feat/nuxtignore
manniL Jan 15, 2019
fba7692
Merge branch 'dev' of github.com:nuxt/nuxt.js into feat/nuxtignore
clarkdo Jan 23, 2019
487b248
feat: support ignore store
clarkdo Jan 23, 2019
6df17d9
test: ignore test
clarkdo Jan 23, 2019
12a67ac
plugins is unnecessary to support ingore
clarkdo Jan 23, 2019
25d5ab6
test: ignore layout
clarkdo Jan 23, 2019
00b7eca
fix: wrong dir in resolving layouts
clarkdo Jan 23, 2019
249daaf
Merge branch 'dev' into feat/nuxtignore
clarkdo Jan 23, 2019
31f81f0
Merge branch 'dev' into feat/nuxtignore
clarkdo Jan 23, 2019
fde1008
Merge branch 'dev' into feat/nuxtignore
clarkdo Jan 28, 2019
3f2ab41
Merge branch 'dev' into feat/nuxtignore
clarkdo Jan 29, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/builder/package.json
Expand Up @@ -16,6 +16,7 @@
"fs-extra": "^7.0.1",
"glob": "^7.1.3",
"hash-sum": "^1.0.2",
"ignore": "^5.0.4",
clarkdo marked this conversation as resolved.
Show resolved Hide resolved
"lodash": "^4.17.11",
"pify": "^4.0.1",
"semver": "^5.6.0",
Expand Down
45 changes: 31 additions & 14 deletions packages/builder/src/builder.js
Expand Up @@ -30,6 +30,7 @@ import {
isString
} from '@nuxt/utils'

import Ignore from './ignore'
import BuildContext from './context'

const glob = pify(Glob)
Expand Down Expand Up @@ -85,6 +86,9 @@ export default class Builder {
// }

this.bundleBuilder = this.getBundleBuilder(bundleBuilder)
this.ignore = new Ignore({
rootDir: this.options.srcDir
})
}

getBundleBuilder(BundleBuilder) {
Expand Down Expand Up @@ -131,6 +135,18 @@ export default class Builder {
)
}

async resolveFiles(dir, cwd = this.options.srcDir) {
return this.ignore.filter(await glob(`${dir}/**/*.{${this.supportedExtensions.join(',')}}`, {
cwd,
ignore: this.options.ignore
}))
}

async resolveRelative(dir) {
const dirPrefix = new RegExp(`^${dir}/`)
return (await this.resolveFiles(dir)).map(file => ({ src: file.replace(dirPrefix, '') }))
}

resolvePlugins() {
// Check plugins exist then set alias to their real path
return Promise.all(this.plugins.map(async (p) => {
Expand Down Expand Up @@ -307,14 +323,12 @@ export default class Builder {
router: this.options.router,
env: this.options.env,
head: this.options.head,
middleware: fsExtra.existsSync(path.join(this.options.srcDir, this.options.dir.middleware)),
store: this.options.store,
globalName: this.options.globalName,
globals: this.globals,
css: this.options.css,
plugins: this.plugins,
appPath: './App.js',
ignorePrefix: this.options.ignorePrefix,
pi0 marked this conversation as resolved.
Show resolved Hide resolved
layouts: Object.assign({}, this.options.layouts),
loading:
typeof this.options.loading === 'string'
Expand All @@ -332,10 +346,7 @@ export default class Builder {

// -- Layouts --
if (fsExtra.existsSync(path.resolve(this.options.srcDir, this.options.dir.layouts))) {
const layoutsFiles = await glob(`${this.options.dir.layouts}/**/*.{${this.supportedExtensions.join(',')}}`, {
cwd: this.options.srcDir,
ignore: this.options.ignore
})
const layoutsFiles = await this.resolveFiles(this.options.dir.layouts)
layoutsFiles.forEach((file) => {
const name = file
.replace(new RegExp(`^${this.options.dir.layouts}/`), '')
Expand Down Expand Up @@ -377,15 +388,13 @@ export default class Builder {
} else if (this._nuxtPages) {
// Use nuxt.js createRoutes bases on pages/
const files = {}
; (await glob(`${this.options.dir.pages}/**/*.{${this.supportedExtensions.join(',')}}`, {
cwd: this.options.srcDir,
ignore: this.options.ignore
})).forEach((f) => {
const key = f.replace(new RegExp(`\\.(${this.supportedExtensions.join('|')})$`), '')
if (/\.vue$/.test(f) || !files[key]) {
files[key] = f.replace(/(['"])/g, '\\$1')
const ext = new RegExp(`\\.(${this.supportedExtensions.join('|')})$`)
pi0 marked this conversation as resolved.
Show resolved Hide resolved
for (const page of await this.resolveFiles(this.options.dir.pages)) {
const key = page.replace(ext, '')
if (/\.vue$/.test(page) || !files[key]) {
manniL marked this conversation as resolved.
Show resolved Hide resolved
files[key] = page.replace(/(['"])/g, '\\$1')
}
})
}
templateVars.router.routes = createRoutes(
Object.values(files),
this.options.srcDir,
Expand Down Expand Up @@ -422,9 +431,13 @@ export default class Builder {
// -- Store --
// Add store if needed
if (this.options.store) {
templateVars.storeModules = await this.resolveRelative(this.options.dir.store)
templatesFiles.push('store.js')
}

// -- Middleware --
templateVars.middleware = await this.resolveRelative(this.options.dir.middleware)

// Resolve template files
const customTemplateFiles = this.options.build.templates.map(
t => t.dst || path.basename(t.src || t)
Expand Down Expand Up @@ -620,6 +633,10 @@ export default class Builder {
...this.options.watch
].map(this.nuxt.resolver.resolveAlias)

if (this.ignore.ignoreFile) {
nuxtRestartWatch.push(this.ignore.ignoreFile)
}

this.watchers.restart = chokidar
.watch(nuxtRestartWatch, this.options.watchers.chokidar)
.on('all', (event, _path) => {
Expand Down
51 changes: 51 additions & 0 deletions packages/builder/src/ignore.js
@@ -0,0 +1,51 @@
import path from 'path'
import fs from 'fs-extra'
import ignore from 'ignore'

export default class Ignore {
constructor(options) {
this.rootDir = options.rootDir
this.addIgnoresRules()
}

static get IGNORE_FILENAME() {
return '.nuxtignore'
}

findIgnoreFile() {
if (!this.ignoreFile) {
const ignoreFile = path.resolve(this.rootDir, Ignore.IGNORE_FILENAME)
if (fs.existsSync(ignoreFile) && fs.statSync(ignoreFile).isFile()) {
this.ignoreFile = ignoreFile
this.ignore = ignore()
}
}
return this.ignoreFile
}

readIgnoreFile() {
if (this.findIgnoreFile()) {
return fs.readFileSync(this.ignoreFile, 'utf8')
}
}

addIgnoresRules() {
const content = this.readIgnoreFile()
if (content) {
this.ignore.add(content)
}
}

filter(paths) {
if (this.ignore) {
return this.ignore.filter([].concat(paths || []))
}
return paths
}

reload() {
delete this.ignore
delete this.ignoreFile
this.addIgnoresRules()
}
}
22 changes: 6 additions & 16 deletions packages/vue-app/template/middleware.js
@@ -1,18 +1,8 @@
<% if (middleware) { %>
const files = require.context('@/<%= dir.middleware %>', false, /^\.\/(?!<%= ignorePrefix %>)[^.]+\.(<%= extensions %>)$/)
const filenames = files.keys()

function getModule(filename) {
const file = files(filename)
return file.default || file
}
const middleware = {}

// Generate the middleware
for (const filename of filenames) {
const name = filename.replace(/^\.\//, '').replace(/\.(<%= extensions %>)$/, '')
middleware[name] = getModule(filename)
}

<% middleware.forEach(m => {
const name = m.src.replace(new RegExp(`\\.(${extensions})$`), '')
%>
middleware['<%= name %>'] = require('@/<%= dir.middleware %>/<%= m.src %>');
middleware['<%= name %>'] = middleware['<%= name %>'].default || middleware['<%= name %>']
<% }) %>
export default middleware
<% } else { %>export default {}<% } %>
134 changes: 64 additions & 70 deletions packages/vue-app/template/store.js
Expand Up @@ -7,75 +7,28 @@ let storeData = {}

let files

void (function updateModules() {
files = require.context('@/<%= dir.store %>', true, /^\.\/(?!<%= ignorePrefix %>)[^.]+\.(<%= extensions %>)$/)
pi0 marked this conversation as resolved.
Show resolved Hide resolved
const filenames = files.keys()

// Check if {dir.store}/index.js exists
const indexFilename = filenames.find(filename => filename.includes('./index.'))

if (indexFilename) {
storeData = getModule(indexFilename)
}

void function updateModules() {
<% storeModules.some(s => {
if(s.src.indexOf('index.') === 0) { %>
storeData = require('@/<%= dir.store %>/<%= s.src %>')
<% return true }}) %>
// If store is not an exported method = modules store
if (typeof storeData !== 'function') {
// Store modules
if (!storeData.modules) {
storeData.modules = {}
}
<% storeModules.forEach(s => {
if(s.src.indexOf('index.') !== 0) { %>
resolveStoreModules(require('@/<%= dir.store %>/<%= s.src %>'), '<%= s.src %>')<% }}) %>

for (const filename of filenames) {
let name = filename.replace(/^\.\//, '').replace(/\.(<%= extensions %>)$/, '')
if (name === 'index') continue

const namePath = name.split(/\//)

name = namePath[namePath.length - 1]
if (['state', 'getters', 'actions', 'mutations'].includes(name)) {
const module = getModuleNamespace(storeData, namePath, true)
appendModule(module, filename, name)
continue
}

// If file is foo/index.js, it should be saved as foo
const isIndex = (name === 'index')
if (isIndex) {
namePath.pop()
}

const module = getModuleNamespace(storeData, namePath)
const fileModule = getModule(filename)

name = namePath.pop()
module[name] = module[name] || {}

// if file is foo.js, existing properties take priority
// because it's the least specific case
if (!isIndex) {
module[name] = Object.assign({}, fileModule, module[name])
module[name].namespaced = true
continue
}

// if file is foo/index.js we want to overwrite properties from foo.js
// but not from appended mods like foo/actions.js
const appendedMods = {}
if (module[name].appends) {
appendedMods.appends = module[name].appends
for (const append of module[name].appends) {
appendedMods[append] = module[name][append]
}
}

module[name] = Object.assign({}, module[name], fileModule, appendedMods)
module[name].namespaced = true
}
// If the environment supports hot reloading...
<% if (isDev) { %>
// If the environment supports hot reloading...
<% if (isDev) { %>
if (process.client && module.hot) {
// Whenever any Vuex module is updated...
module.hot.accept(files.id, () => {
module.hot.accept([<% storeModules.forEach(s => { %>
'@/<%= dir.store %>/<%= s.src %>',<% }) %>
], () => {
// Update `root.modules` with the latest definitions.
updateModules()
// Trigger a hot update in the store.
Expand All @@ -86,7 +39,7 @@ void (function updateModules() {
const log = (process.server ? require('consola') : console)
log.warn('Classic mode for store/ is deprecated and will be removed in Nuxt 3.')
}
})()
}()

// createStore
export const createStore = storeData instanceof Function ? storeData : () => {
Expand All @@ -97,17 +50,59 @@ export const createStore = storeData instanceof Function ? storeData : () => {
}))
}

function resolveStoreModules(fileModule, filename) {
let name = filename.replace(/\.(<%= extensions %>)$/, '')
const namePath = name.split(/\//)

name = namePath[namePath.length - 1]
if (['state', 'getters', 'actions', 'mutations'].includes(name)) {
const module = getModuleNamespace(storeData, namePath, true)
appendModule(module, fileModule, name)
return
}

// If file is foo/index.js, it should be saved as foo
const isIndex = (name === 'index')
if (isIndex) {
namePath.pop()
}

const module = getModuleNamespace(storeData, namePath)
checkModule(fileModule, filename)

name = namePath.pop()
module[name] = module[name] || {}

// if file is foo.js, existing properties take priority
// because it's the least specific case
if (!isIndex) {
module[name] = Object.assign({}, fileModule, module[name])
module[name].namespaced = true
return
}

// if file is foo/index.js we want to overwrite properties from foo.js
// but not from appended mods like foo/actions.js
const appendedMods = {}
if (module[name].appends) {
appendedMods.appends = module[name].appends
for (const append of module[name].appends) {
appendedMods[append] = module[name][append]
}
}

module[name] = Object.assign({}, module[name], fileModule, appendedMods)
module[name].namespaced = true
}

// Dynamically require module
function getModule(filename) {
const file = files(filename)
const module = file.default || file
function checkModule(module, filename) {
if (module.commit) {
throw new Error('[nuxt] <%= dir.store %>/' + filename.replace('./', '') + ' should export a method which returns a Vuex instance.')
throw new Error('[nuxt] <%= dir.store %>/' + filename + ' 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 <%= dir.store %>/' + filename.replace('./', ''))
throw new Error('[nuxt] state should be a function in <%= dir.store %>/' + filename)
}
return module
}

function getModuleNamespace(storeData, namePath, forAppend = false) {
Expand All @@ -124,9 +119,8 @@ function getModuleNamespace(storeData, namePath, forAppend = false) {
return getModuleNamespace(storeData.modules[namespace], namePath, forAppend)
}

function appendModule(module, filename, name) {
const file = files(filename)
function appendModule(module, subModule, name) {
module.appends = module.appends || []
module.appends.push(name)
module[name] = file.default || file
module[name] = subModule.default || subModule
}
1 change: 1 addition & 0 deletions test/fixtures/with-config/.nuxtignore
@@ -0,0 +1 @@
pages/test-ignore.vue
Empty file.
5 changes: 5 additions & 0 deletions test/unit/with-config.test.js
Expand Up @@ -180,6 +180,11 @@ describe('with-config', () => {
.rejects.toMatchObject({ statusCode: 404 })
})

test('should ignore files in .nuxtignore', async () => {
await expect(rp(url('/test-ignore')))
.rejects.toMatchObject({ statusCode: 404 })
})

test('renderAndGetWindow options', async () => {
const fakeErrorLog = jest.fn()
const mockOptions = {
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Expand Up @@ -5382,7 +5382,7 @@ ignore@^4.0.6:
resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==

ignore@^5.0.2:
ignore@^5.0.2, ignore@^5.0.4:
version "5.0.4"
resolved "https://registry.npmjs.org/ignore/-/ignore-5.0.4.tgz#33168af4a21e99b00c5d41cbadb6a6cb49903a45"
integrity sha512-WLsTMEhsQuXpCiG173+f3aymI43SXa+fB1rSfbzyP4GkPP+ZFVuO0/3sFUGNBtifisPeDcl/uD/Y2NxZ7xFq4g==
Expand Down