From 96bdcaba01ebd049b3ab5b56d1a2c2054bb8dc62 Mon Sep 17 00:00:00 2001 From: Pim Date: Wed, 24 Oct 2018 15:46:06 +0200 Subject: [PATCH] fix(app): lint all templates (#4175) --- .eslintrc.js | 29 +++++- package.json | 2 +- .../app/template}/.eslintignore | 0 packages/app/template/App.js | 18 ++-- packages/app/template/client.js | 91 +++++++++-------- packages/app/template/components/no-ssr.js | 2 +- .../app/template/components/nuxt-child.js | 17 +++- .../app/template/components/nuxt-error.vue | 41 ++++---- packages/app/template/components/nuxt-link.js | 3 +- .../app/template/components/nuxt-loading.vue | 41 ++++---- packages/app/template/components/nuxt.js | 15 +-- packages/app/template/index.js | 29 +++--- packages/app/template/layouts/default.vue | 2 +- packages/app/template/middleware.js | 2 +- packages/app/template/router.js | 51 +++++----- packages/app/template/server.js | 28 +++--- packages/app/template/store.js | 6 +- packages/app/template/utils.js | 98 +++++++++---------- .../app/template/views/loading/default.html | 6 +- packages/builder/src/builder.js | 20 ++-- packages/common/src/nuxt.config.js | 1 + packages/common/src/utils.js | 15 +++ test/fixtures/cli/cli.build.config.js | 1 + test/fixtures/cli/cli.gen.config.js | 5 +- test/utils/index.js | 1 + 25 files changed, 304 insertions(+), 220 deletions(-) rename {lib/app => packages/app/template}/.eslintignore (100%) diff --git a/.eslintrc.js b/.eslintrc.js index c8a982971d..986bfba056 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,5 +6,32 @@ module.exports = { }, extends: [ '@nuxtjs' - ] + ], + overrides: [{ + files: [ 'test/fixtures/*/.nuxt*/**' ], + rules: { + 'vue/name-property-casing': ['error', 'kebab-case'] + } + }, { + files: [ 'test/fixtures/*/.nuxt*/**/+(App|index).js' ], + rules: { + 'import/order': 'ignore' + } + }, { + files: [ 'test/fixtures/*/.nuxt*/**/client.js' ], + rules: { + 'no-console': ['error', { allow: ['error'] }] + } + }, { + files: [ 'test/fixtures/*/.nuxt*/**/router.js' ], + rules: { + 'no-console': ['error', { allow: ['warn'] }] + } + }, { + files: [ 'test/fixtures/*/.nuxt*/**/*.html' ], + rules: { + 'semi': ['error', 'always', { 'omitLastInOneLineBlock': true }], + 'no-var': 'warn' + } + }] } diff --git a/package.json b/package.json index 5145723f65..8b537ef22d 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dev": "yarn build --watch", "coverage": "codecov", "lint": "eslint --ext .js,.mjs,.vue .", - "lint:app": "eslint-multiplexer eslint --ignore-path lib/app/.eslintignore 'test/fixtures/!(missing-plugin)/.nuxt!(-dev)/**' | eslint-multiplexer -b", + "lint:app": "eslint-multiplexer eslint --ignore-path packages/app/template/.eslintignore 'test/fixtures/!(missing-plugin)/.nuxt!(-dev)/**' | eslint-multiplexer -b", "nuxt": "node -r esm ./packages/cli/bin/nuxt.js", "test": "yarn test:fixtures && yarn test:unit", "test:fixtures": "jest test/fixtures", diff --git a/lib/app/.eslintignore b/packages/app/template/.eslintignore similarity index 100% rename from lib/app/.eslintignore rename to packages/app/template/.eslintignore diff --git a/packages/app/template/App.js b/packages/app/template/App.js index be50ecb55d..1b5c33767b 100644 --- a/packages/app/template/App.js +++ b/packages/app/template/App.js @@ -12,12 +12,14 @@ import '<%= relativeToBuild(resolvePath(c.src || c)) %>' } }).join('\n') %> -const layouts = { <%= Object.keys(layouts).map(key => `"_${key}": _${hash(key)}`).join(',') %> } +const layouts = { <%= Object.keys(layouts).map(key => `"_${key}": _${hash(key)}`).join(',') %> }<%= isTest ? '// eslint-disable-line' : '' %> <% if (splitChunks.layouts) { %>let resolvedLayouts = {}<% } %> export default { + <%= isTest ? '/* eslint-disable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, object-property-newline, arrow-parens */' : '' %> head: <%= serialize(head).replace(/:\w+\(/gm, ':function(') %>, + <%= isTest ? '/* eslint-enable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, object-property-newline, arrow-parens */' : '' %> render(h, props) { <% if (loading) { %>const loadingEl = h('nuxt-loading', { ref: 'loading' })<% } %> const layoutEl = h(this.layout || 'nuxt') @@ -35,7 +37,7 @@ export default { } }, [ templateEl ]) - return h('div',{ + return h('div', { domProps: { id: '<%= globals.id %>' } @@ -48,10 +50,10 @@ export default { layout: null, layoutName: '' }), - beforeCreate () { + beforeCreate() { Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt) }, - created () { + created() { // Add this.$nuxt in child instances Vue.prototype.<%= globals.nuxt %> = this // add to window so we can listen when ready @@ -65,7 +67,7 @@ export default { this.error = this.nuxt.error }, <% if (loading) { %> - mounted () { + mounted() { this.$loading = this.$refs.loading }, watch: { @@ -74,7 +76,7 @@ export default { <% } %> methods: { <% if (loading) { %> - errorChanged () { + errorChanged() { if (this.nuxt.err && this.$loading) { if (this.$loading.fail) this.$loading.fail() if (this.$loading.finish) this.$loading.finish() @@ -82,7 +84,7 @@ export default { }, <% } %> <% if (splitChunks.layouts) { %> - setLayout (layout) { + setLayout(layout) { <% if (debug) { %> if(layout && typeof layout !== 'string') throw new Error('[nuxt] Avoid using non-string value as layout property.') <% } %> @@ -92,7 +94,7 @@ export default { this.layout = resolvedLayouts[_layout] return this.layout }, - loadLayout (layout) { + loadLayout(layout) { const undef = !layout const inexisting = !(layouts['_' + layout] || resolvedLayouts['_' + layout]) let _layout = '_' + ((undef || inexisting) ? 'default' : layout) diff --git a/packages/app/template/client.js b/packages/app/template/client.js index 5f5514fbdf..1b4d5bb3e4 100644 --- a/packages/app/template/client.js +++ b/packages/app/template/client.js @@ -1,6 +1,5 @@ import Vue from 'vue' import middleware from './middleware' -import { createApp, NuxtError } from './index' import { applyAsyncData, sanitizeComponent, @@ -16,6 +15,7 @@ import { getQueryDiff, globalHandleError } from './utils' +import { createApp, NuxtError } from './index' const noopData = () => { return {} } const noopFetch = () => {} @@ -29,7 +29,7 @@ let router // Try to rehydrate SSR data from window const NUXT = window.<%= globals.context %> || {} -Object.assign(Vue.config, <%= serialize(vue.config) %>) +Object.assign(Vue.config, <%= serialize(vue.config) %>)<%= isTest ? '// eslint-disable-line' : '' %> <% if (debug || mode === 'spa') { %> // Setup global Vue error handler @@ -46,7 +46,7 @@ if (!Vue.config.$nuxt) { if (typeof defaultErrorHandler === 'function') { handled = defaultErrorHandler(err, vm, info, ...rest) } - if (handled === true){ + if (handled === true) { return handled } @@ -79,10 +79,10 @@ Vue.config.$nuxt.<%= globals.nuxt %> = true // Create and mount App createApp() -.then(mountApp) -.catch((err) => { - console.error('[nuxt] Error while initializing app', err) -}) + .then(mountApp) + .catch((err) => { + console.error('[nuxt] Error while initializing app', err) + }) function componentOption(component, key, ...args) { if (!component || !component.options || !component.options[key]) { @@ -107,17 +107,17 @@ function mapTransitions(Components, to, from) { // Combine transitions & prefer `leave` transitions of 'from' route if (from && from.matched.length && from.matched[0].components.default) { - const from_transitions = componentTransitions(from.matched[0].components.default) - Object.keys(from_transitions) - .filter((key) => from_transitions[key] && key.toLowerCase().includes('leave')) - .forEach((key) => { transitions[key] = from_transitions[key] }) + const fromTransitions = componentTransitions(from.matched[0].components.default) + Object.keys(fromTransitions) + .filter(key => fromTransitions[key] && key.toLowerCase().includes('leave')) + .forEach((key) => { transitions[key] = fromTransitions[key] }) } return transitions }) } -async function loadAsyncComponents (to, from, next) { +async function loadAsyncComponents(to, from, next) { // Check if route path changed (this._pathChanged), only if the page is not an error (for validate()) this._pathChanged = !!app.nuxt.err || from.path !== to.path this._queryChanged = JSON.stringify(to.query) !== JSON.stringify(from.query) @@ -138,7 +138,7 @@ async function loadAsyncComponents (to, from, next) { const watchQuery = Component.options.watchQuery if (watchQuery === true) return true if (Array.isArray(watchQuery)) { - return watchQuery.some((key) => this._diffQuery[key]) + return watchQuery.some(key => this._diffQuery[key]) } return false }) @@ -150,10 +150,10 @@ async function loadAsyncComponents (to, from, next) { // Call next() next() } catch (err) { - err = err || {} - const statusCode = (err.statusCode || err.status || (err.response && err.response.status) || 500) - this.error({ statusCode, message: err.message }) - this.<%= globals.nuxt %>.$emit('routeChanged', to, from, err) + const error = err || {} + const statusCode = (error.statusCode || error.status || (error.response && error.response.status) || 500) + this.error({ statusCode, message: error.message }) + this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error) next(false) } } @@ -182,8 +182,8 @@ function resolveComponents(router) { }) } -function callMiddleware (Components, context, layout) { - let midd = <%= devalue(router.middleware) %> +function callMiddleware(Components, context, layout) { + let midd = <%= devalue(router.middleware) %><%= isTest ? '// eslint-disable-line' : '' %> let unknownMiddleware = false // If layout is undefined, only call global middleware @@ -212,7 +212,7 @@ function callMiddleware (Components, context, layout) { return middlewareSeries(midd, context) } -async function render (to, from, next) { +async function render(to, from, next) { if (this._pathChanged === false && this._queryChanged === false) return next() // Handle first render on SPA mode if (to === from) _lastPaths = [] @@ -337,21 +337,21 @@ async function render (to, from, next) { Component._dataRefresh = false // Check if Component need to be refreshed (call asyncData & fetch) // Only if its slug has changed or is watch query changes - if (this._pathChanged && this._queryChanged || Component._path !== _lastPaths[i]) { + if ((this._pathChanged && this._queryChanged) || Component._path !== _lastPaths[i]) { Component._dataRefresh = true } else if (!this._pathChanged && this._queryChanged) { const watchQuery = Component.options.watchQuery if (watchQuery === true) { Component._dataRefresh = true } else if (Array.isArray(watchQuery)) { - Component._dataRefresh = watchQuery.some((key) => this._diffQuery[key]) + Component._dataRefresh = watchQuery.some(key => this._diffQuery[key]) } } if (!this._hadError && this._isMounted && !Component._dataRefresh) { return Promise.resolve() } - let promises = [] + const promises = [] const hasAsyncData = ( Component.options.asyncData && @@ -368,7 +368,7 @@ async function render (to, from, next) { .then((asyncDataResult) => { applyAsyncData(Component, asyncDataResult) <% if (loading) { %> - if(this.$loading.increase) { + if (this.$loading.increase) { this.$loading.increase(loadingIncrease) } <% } %> @@ -381,18 +381,18 @@ async function render (to, from, next) { // Call fetch(context) if (hasFetch) { - let p = Component.options.fetch(app.context) - if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) { - p = Promise.resolve(p) + let p = Component.options.fetch(app.context) + if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) { + p = Promise.resolve(p) + } + p.then((fetchResult) => { + <% if (loading) { %> + if (this.$loading.increase) { + this.$loading.increase(loadingIncrease) } - p.then((fetchResult) => { - <% if (loading) { %> - if (this.$loading.increase) { - this.$loading.increase(loadingIncrease) - } - <% } %> - }) - promises.push(p) + <% } %> + }) + promises.push(p) } return Promise.all(promises) @@ -408,10 +408,9 @@ async function render (to, from, next) { next() } - } catch (error) { - if (!error) { - error = {} - } else if (error.message === 'ERR_REDIRECT') { + } catch (err) { + const error = err || {} + if (error.message === 'ERR_REDIRECT') { return this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error) } _lastPaths = [] @@ -434,7 +433,7 @@ async function render (to, from, next) { } // Fix components format in matched, it's due to code-splitting of vue-router -function normalizeComponents (to, ___) { +function normalizeComponents(to, ___) { flatMapComponents(to, (Component, _, match, key) => { if (typeof Component === 'object' && !Component.options) { // Updated via vue-router resolveAsyncComponents() @@ -486,7 +485,7 @@ function fixPrepatch(to, ___) { typeof instance.constructor.options.data === 'function' ) { const newData = instance.constructor.options.data.call(instance) - for (let key in newData) { + for (const key in newData) { Vue.set(instance.$data, key, newData[key]) } } @@ -499,7 +498,7 @@ function fixPrepatch(to, ___) { }) } -function nuxtReady (_app) { +function nuxtReady(_app) { window.<%= globals.readyCallback %>Cbs.forEach((cb) => { if (typeof cb === 'function') { cb(_app) @@ -531,7 +530,7 @@ function getNuxtChildComponents($parent, $components = []) { return $components } -function hotReloadAPI (_app) { +function hotReloadAPI(_app) { if (!module.hot) return let $components = getNuxtChildComponents(_app.<%= globals.nuxt %>, []) @@ -539,7 +538,7 @@ function hotReloadAPI (_app) { $components.forEach(addHotReload.bind(_app)) } -function addHotReload ($component, depth) { +function addHotReload($component, depth) { if ($component.$vnode.data._hasHotReload) return $component.$vnode.data._hasHotReload = true @@ -617,7 +616,7 @@ async function mountApp(__app) { // Set global variables app = __app.app router = __app.router - <% if (store) { %>store = __app.store <% } %> + <% if (store) { %>store = __app.store<% } %> // Resolve route components const Components = await Promise.all(resolveComponents(router)) @@ -626,7 +625,7 @@ async function mountApp(__app) { const _app = new Vue(app) <% if (mode !== 'spa') { %> - // Load layout + // Load layout const layout = NUXT.layout || 'default' await _app.loadLayout(layout) _app.setLayout(layout) diff --git a/packages/app/template/components/no-ssr.js b/packages/app/template/components/no-ssr.js index db65103194..ea27ead1b4 100644 --- a/packages/app/template/components/no-ssr.js +++ b/packages/app/template/components/no-ssr.js @@ -1,4 +1,4 @@ -/* +/*<%= isTest ? ' @vue/component' : '' %> ** From https://github.com/egoist/vue-no-ssr ** With the authorization of @egoist */ diff --git a/packages/app/template/components/nuxt-child.js b/packages/app/template/components/nuxt-child.js index 59ab308bde..f57898c8f7 100644 --- a/packages/app/template/components/nuxt-child.js +++ b/packages/app/template/components/nuxt-child.js @@ -1,8 +1,15 @@ +<%= isTest ? '// @vue/component' : '' %> export default { name: 'nuxt-child', functional: true, - props: ['keepAlive', 'keepAliveProps'], - render (h, { parent, data, props }) { + props: { + nuxtChildKey: { + type: String, + default: '' + }, + keepAlive: Boolean + }, + render(h, { parent, data, props }) { data.nuxtChild = true const _parent = parent const transitions = parent.<%= globals.nuxt %>.nuxt.transitions @@ -17,14 +24,14 @@ export default { } data.nuxtChildDepth = depth const transition = transitions[depth] || defaultTransition - let transitionProps = {} + const transitionProps = {} transitionsKeys.forEach((key) => { if (typeof transition[key] !== 'undefined') { transitionProps[key] = transition[key] } }) - let listeners = {} + const listeners = {} listenersKeys.forEach((key) => { if (typeof transition[key] === 'function') { listeners[key] = transition[key].bind(_parent) @@ -43,7 +50,7 @@ export default { let routerView = [ h('router-view', data) ] - if (typeof props.keepAlive !== 'undefined') { + if (props.keepAlive) { routerView = [ h('keep-alive', { props: props.keepAliveProps }, routerView) ] diff --git a/packages/app/template/components/nuxt-error.vue b/packages/app/template/components/nuxt-error.vue index bfeb8564ec..301cca0ff3 100644 --- a/packages/app/template/components/nuxt-error.vue +++ b/packages/app/template/components/nuxt-error.vue @@ -1,28 +1,33 @@ diff --git a/packages/builder/src/builder.js b/packages/builder/src/builder.js index c7bf7ef574..c46bde1f6d 100644 --- a/packages/builder/src/builder.js +++ b/packages/builder/src/builder.js @@ -33,7 +33,8 @@ import { sequence, relativeTo, waitFor, - determineGlobals + determineGlobals, + stripWhitespace } from '@nuxt/common' import PerfLoader from './webpack/utils/perf-loader' @@ -214,6 +215,7 @@ export default class Builder { splitChunks: this.options.build.splitChunks, uniqBy, isDev: this.options.dev, + isTest: this.options.test, debug: this.options.debug, vue: { config: this.options.vue.config }, mode: this.options.mode, @@ -432,13 +434,15 @@ export default class Builder { }, interpolate: /<%=([\s\S]+?)%>/g }) - content = templateFunction( - Object.assign({}, templateVars, { - options: options || {}, - custom, - src, - dst - }) + content = stripWhitespace( + templateFunction( + Object.assign({}, templateVars, { + options: options || {}, + custom, + src, + dst + }) + ) ) } catch (err) { /* istanbul ignore next */ diff --git a/packages/common/src/nuxt.config.js b/packages/common/src/nuxt.config.js index 243d998724..51566a9433 100644 --- a/packages/common/src/nuxt.config.js +++ b/packages/common/src/nuxt.config.js @@ -10,6 +10,7 @@ const nuxtDir = fs.existsSync(path.resolve(__dirname, '..', '..', 'package.js')) export default { // Information about running environment dev: Boolean(env.dev), + test: Boolean(env.test), debug: undefined, // = dev // Mode diff --git a/packages/common/src/utils.js b/packages/common/src/utils.js index fe17e0b089..7efdc33450 100644 --- a/packages/common/src/utils.js +++ b/packages/common/src/utils.js @@ -389,3 +389,18 @@ const DYNAMIC_ROUTE_REGEX = /^\/(:|\*)/ * @return {array} */ export const wrapArray = value => Array.isArray(value) ? value : [value] + +const WHITESPACE_REPLACEMENTS = [ + [/[ \t\f\r]+\n/g, '\n'], // strip empty indents + [/{\n{2,}/g, '{\n'], // strip start padding from blocks + [/\n{2,}([ \t\f\r]*})/g, '\n$1'], // strip end padding from blocks + [/\n{3,}/g, '\n\n'], // strip multiple blank lines (1 allowed) + [/\n{2,}$/g, '\n'] // strip blank lines EOF (0 allowed) +] + +export const stripWhitespace = function stripWhitespace(string) { + WHITESPACE_REPLACEMENTS.forEach(([regex, newSubstr]) => { + string = string.replace(regex, newSubstr) + }) + return string +} diff --git a/test/fixtures/cli/cli.build.config.js b/test/fixtures/cli/cli.build.config.js index 683f83fe8d..3e59ef4cd6 100644 --- a/test/fixtures/cli/cli.build.config.js +++ b/test/fixtures/cli/cli.build.config.js @@ -1,4 +1,5 @@ export default { + test: true, hooks(hook) { hook('build:done', () => { process.stdout.write('Compiled successfully') diff --git a/test/fixtures/cli/cli.gen.config.js b/test/fixtures/cli/cli.gen.config.js index cac6bde12a..da5966cdb4 100644 --- a/test/fixtures/cli/cli.gen.config.js +++ b/test/fixtures/cli/cli.gen.config.js @@ -1,7 +1,8 @@ export default { - buildDir: '.nuxt-generate/.build', + test: true, + buildDir: '.nuxt-generate/build', generate: { - dir: '.nuxt-generate/.generate' + dir: '.nuxt-generate/generate' }, hooks(hook) { hook('generate:done', (generator, errors) => { diff --git a/test/utils/index.js b/test/utils/index.js index 8932e6f008..83efc6acf4 100644 --- a/test/utils/index.js +++ b/test/utils/index.js @@ -30,6 +30,7 @@ export const loadFixture = async function (fixture, overrides) { config.rootDir = rootDir config.dev = false + config.test = true return defaultsDeep({}, overrides, config) }