├── .gitignore ├── .npmignore ├── README.md ├── helpers ├── assert-rule.js ├── check-for-deprecations.js ├── check-if-outdated.js ├── check-if-outdated.spec.js ├── migration-guide-url-for.js ├── migration-guide-url-for.spec.js ├── parent-folder-name-for.js ├── parent-folder-name-for.spec.js ├── print-summary.js ├── print-summary.spec.js ├── report-warning.js └── report-warning.spec.js ├── index.js ├── jasmine.json ├── package.json ├── rules ├── enforce-warning-type.spec.js ├── vue-router │ ├── arbitrary-route-properties.js │ ├── arbitrary-route-properties.spec.js │ ├── canreuse-false.js │ ├── canreuse-false.spec.js │ ├── hashbang-false.js │ ├── hashbang-false.spec.js │ ├── history-and-abstract-modes.js │ ├── history-and-abstract-modes.spec.js │ ├── loading-route-data.js │ ├── loading-route-data.spec.js │ ├── root-option.js │ ├── root-option.spec.js │ ├── route-path-one-or-more-named-parameters.js │ ├── route-path-one-or-more-named-parameters.spec.js │ ├── router-alias.js │ ├── router-alias.spec.js │ ├── router-go.js │ ├── router-go.spec.js │ ├── router-lifecycle-data.js │ ├── router-lifecycle-data.spec.js │ ├── router-lifecycle-hooks.js │ ├── router-lifecycle-hooks.spec.js │ ├── router-map.js │ ├── router-map.spec.js │ ├── router-on.js │ ├── router-on.spec.js │ ├── router-redirect.js │ ├── router-redirect.spec.js │ ├── router-start.js │ ├── router-start.spec.js │ ├── save-scroll-position.js │ ├── save-scroll-position.spec.js │ ├── subroutes.js │ ├── subroutes.spec.js │ ├── suppress-transition-error.js │ ├── suppress-transition-error.spec.js │ ├── transition-on-load.js │ ├── transition-on-load.spec.js │ ├── v-link-active.js │ ├── v-link-active.spec.js │ ├── v-link.js │ ├── v-link.spec.js │ ├── vue-router-dependency.js │ └── vue-router-dependency.spec.js ├── vue │ ├── array-prototype-remove.js │ ├── array-prototype-remove.spec.js │ ├── array-prototype-set.js │ ├── array-prototype-set.spec.js │ ├── built-in-filters.js │ ├── built-in-filters.spec.js │ ├── cache-false.js │ ├── cache-false.spec.js │ ├── data-assignment.js │ ├── data-assignment.spec.js │ ├── debounce-param.js │ ├── debounce-param.spec.js │ ├── directive-filters.js │ ├── directive-filters.spec.js │ ├── directive-literal-modifier.js │ ├── directive-literal-modifier.spec.js │ ├── dispatch-and-broadcast.js │ ├── dispatch-and-broadcast.spec.js │ ├── filter-arguments-without-parentheses.js │ ├── filter-arguments-without-parentheses.spec.js │ ├── html-interpolation.js │ ├── html-interpolation.spec.js │ ├── interpolation-within-attributes-no-quotes.js │ ├── interpolation-within-attributes-no-quotes.spec.js │ ├── interpolation-within-attributes.js │ ├── interpolation-within-attributes.spec.js │ ├── keep-alive-attribute.js │ ├── keep-alive-attribute.spec.js │ ├── lazy-and-number-params.js │ ├── lazy-and-number-params.spec.js │ ├── lifecycle-hooks.js │ ├── lifecycle-hooks.spec.js │ ├── named-slot-styling.js │ ├── named-slot-styling.spec.js │ ├── one-time-binding.js │ ├── one-time-binding.spec.js │ ├── prop-coerce.js │ ├── prop-coerce.spec.js │ ├── replace-false.js │ ├── replace-false.spec.js │ ├── style-with-inline-important.js │ ├── style-with-inline-important.spec.js │ ├── track-by.js │ ├── track-by.spec.js │ ├── transition.js │ ├── transition.spec.js │ ├── twoway-true.js │ ├── twoway-true.spec.js │ ├── v-bind-once.js │ ├── v-bind-once.spec.js │ ├── v-bind-sync.js │ ├── v-bind-sync.spec.js │ ├── v-el-and-v-ref.js │ ├── v-el-and-v-ref.spec.js │ ├── v-el-els.js │ ├── v-el-els.spec.js │ ├── v-for-argument-order-with-object.js │ ├── v-for-argument-order-with-object.spec.js │ ├── v-for-argument-order.js │ ├── v-for-argument-order.spec.js │ ├── v-for-implicit-index-and-key.js │ ├── v-for-implicit-index-and-key.spec.js │ ├── v-for-v-model-primitive-value.js │ ├── v-for-v-model-primitive-value.spec.js │ ├── v-leave-class.js │ ├── v-leave-class.spec.js │ ├── v-on-keycodes.js │ ├── v-on-keycodes.spec.js │ ├── v-show-with-v-else.js │ ├── v-show-with-v-else.spec.js │ ├── v-transition-class.js │ ├── v-transition-class.spec.js │ ├── vm-delete.js │ ├── vm-delete.spec.js │ ├── vm-dom-methods.js │ ├── vm-dom-methods.spec.js │ ├── vm-eval.js │ ├── vm-eval.spec.js │ ├── vm-get.js │ ├── vm-get.spec.js │ ├── vm-interpolate.js │ ├── vm-interpolate.spec.js │ ├── vm-log.js │ ├── vm-log.spec.js │ ├── vm-set.js │ ├── vm-set.spec.js │ ├── vue-config-async.js │ ├── vue-config-async.spec.js │ ├── vue-config-debug.js │ ├── vue-config-debug.spec.js │ ├── vue-config-delimiters.js │ ├── vue-config-delimiters.spec.js │ ├── vue-config-unsafe-delimiters.js │ ├── vue-config-unsafe-delimiters.spec.js │ ├── vue-delete-vm.js │ ├── vue-delete-vm.spec.js │ ├── vue-dependency.js │ ├── vue-dependency.spec.js │ ├── vue-directive.js │ ├── vue-directive.spec.js │ ├── vue-element-directive.js │ ├── vue-element-directive.spec.js │ ├── vue-loader-dependency.js │ ├── vue-loader-dependency.spec.js │ ├── vue-partial.js │ ├── vue-partial.spec.js │ ├── vue-set-vm.js │ ├── vue-set-vm.spec.js │ ├── vue-transition.js │ └── vue-transition.spec.js └── vuex │ ├── store-event-emitter.js │ ├── store-event-emitter.spec.js │ ├── store-watch-with-string.js │ ├── store-watch-with-string.spec.js │ ├── vuex-dependency.js │ ├── vuex-dependency.spec.js │ ├── vuex-middlewares.js │ └── vuex-middlewares.spec.js └── spec ├── e2e └── basic.spec.js ├── fixtures └── projects │ ├── empty-file │ └── main.js │ ├── ignorable │ ├── main.js │ └── style.css │ ├── package-json │ └── package.json │ ├── simple │ └── main.js │ └── weird-extension │ └── some-file.blarg └── helpers ├── catch-log.js ├── create-rule-checker.js └── run-migration-helper.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | **/*.log 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | spec 2 | **/*.spec.js 3 | jasmine.json 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-migration-helper 2 | 3 | > CLI tool to aid in migration from Vue 1.x to 2.0. It scans files for Vue-specific code and provides detailed warnings when deprecated patterns are found. It cannot reliably catch _every_ deprecation, but should get you 80% of the way there. 4 | 5 | ![Screenshot](http://i.imgur.com/aHh5TfR.png) 6 | 7 | ## Usage 8 | 9 | ``` sh 10 | # install 11 | npm install --global vue-migration-helper 12 | 13 | # navigate to a Vue 1.x project directory 14 | cd path/to/my-vue-project 15 | 16 | # scan all files in the current directory 17 | vue-migration-helper 18 | # scan all files in specific sub-directories 19 | vue-migration-helper src folder-a folder-b 20 | # scan a specific file or files 21 | vue-migration-helper src/app.vue 22 | ``` 23 | 24 | When you want to update, just run the install again: 25 | 26 | ``` sh 27 | npm install --global vue-migration-helper 28 | ``` 29 | 30 | ## Contributing 31 | 32 | While the binary supports Node v4+, tests are written to take advantage of v6+, so you'll need that installed. 33 | 34 | ``` sh 35 | # clone the project 36 | git clone https://github.com/vuejs/vue-migration-helper.git 37 | 38 | # navigate to directory 39 | cd vue-migration-helper 40 | 41 | # install the dependencies 42 | npm install 43 | ``` 44 | 45 | Then write a failing test for a rule you'd like to improve. Confirm that it fails with: 46 | 47 | ``` sh 48 | # run all tests 49 | npm run test -s 50 | ``` 51 | 52 | If your regex skills aren't strong enough to fix the problem yourself, just submit a pull request with the failing test and we'll take it from there. Before you do though, make sure to lint the project for typos and style violations: 53 | 54 | ``` sh 55 | # lint all files 56 | npm run lint -s 57 | ``` 58 | -------------------------------------------------------------------------------- /helpers/assert-rule.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function oldSchoolSpread (array) { 4 | return Object.prototype.toString.call(array) === '[object Array]' 5 | ? array 6 | : [].slice.call(array) 7 | } 8 | 9 | module.exports = function (fileData, rule) { 10 | var matches = fileData.line.match(rule.pattern) 11 | return matches && 12 | rule.warning.apply(null, oldSchoolSpread(matches)) 13 | } 14 | -------------------------------------------------------------------------------- /helpers/check-for-deprecations.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var recursiveReadSync = require('recursive-readdir-sync') 4 | var path = require('path') 5 | var assertRule = require('./assert-rule') 6 | var reportWarning = require('./report-warning') 7 | 8 | var rulesPath = path.join(__dirname, '../rules') 9 | var rules = recursiveReadSync(rulesPath) 10 | .filter(function (file) { 11 | return ( 12 | file.indexOf('.js') === file.length - 3 && 13 | file.indexOf('.spec') === -1 14 | ) 15 | }) 16 | .map(function (file) { 17 | var rule = require(file) 18 | rule.file = file 19 | return rule 20 | }) 21 | 22 | var styleExtensions = [ 23 | 'css', 'postcss', 'scss', 'sass', 'styl', 'stylus', 'less' 24 | ] 25 | 26 | var ignorableExtensions = { 27 | template: styleExtensions, 28 | js: styleExtensions, 29 | style: ['js'] 30 | } 31 | 32 | function fileHasExtension (file, extension) { 33 | return path.extname(file).slice(1) === extension 34 | } 35 | 36 | function shouldIgnoreWarning (file, warning) { 37 | if (warning.type === 'package.json') { 38 | return path.basename(file) !== warning.type 39 | } else { 40 | return ignorableExtensions[warning.type].some(function (extension) { 41 | return fileHasExtension(file, extension) 42 | }) 43 | } 44 | } 45 | 46 | module.exports = function (fileData) { 47 | return rules.some(function (rule) { 48 | var warning = assertRule(fileData, rule) 49 | if (warning && !shouldIgnoreWarning(fileData.file, warning)) { 50 | reportWarning(fileData, warning, rule) 51 | return true 52 | } 53 | return false 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /helpers/check-if-outdated.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var packageConfig = require('../package.json') 4 | var request = require('request') 5 | var semver = require('semver') 6 | var chalk = require('chalk') 7 | 8 | function checkNodeVersion () { 9 | function parseVersionNumber (versionString) { 10 | return parseFloat(versionString.replace(/[^\d\.]/g, '')) 11 | } 12 | 13 | var minNodeVersion = parseVersionNumber(packageConfig.engines.node) 14 | var currentNodeVersion = parseVersionNumber(process.version) 15 | if (minNodeVersion > currentNodeVersion) { 16 | console.log(chalk.red( 17 | 'You must upgrade node to >=' + minNodeVersion + ' to use vue-migration-helper\n' 18 | )) 19 | process.exit(1) 20 | } 21 | } 22 | 23 | function checkMigrationHelperVersion (done) { 24 | request({ 25 | url: 'https://registry.npmjs.org/vue-migration-helper', 26 | timeout: 1000 27 | }, function (error, response, body) { 28 | if (!error && response.statusCode === 200) { 29 | var latestVersion = JSON.parse(body)['dist-tags'].latest 30 | var localVersion = packageConfig.version 31 | if (semver.lt(localVersion, latestVersion)) { 32 | console.log() 33 | console.log(chalk.yellow('A newer version of vue-migration-helper is available.')) 34 | console.log() 35 | console.log(' latest: ' + chalk.green(latestVersion)) 36 | console.log(' installed: ' + chalk.red(localVersion)) 37 | console.log() 38 | console.log('Please update with:') 39 | console.log() 40 | console.log(chalk.green( 41 | ' npm update --global vue-migration-helper' 42 | )) 43 | process.exit(1) 44 | } 45 | } 46 | done() 47 | }) 48 | } 49 | 50 | function checkIfOutdated (done) { 51 | checkNodeVersion() 52 | checkMigrationHelperVersion(done) 53 | } 54 | 55 | checkIfOutdated.checkNodeVersion = checkNodeVersion 56 | checkIfOutdated.checkMigrationHelperVersion = checkMigrationHelperVersion 57 | 58 | module.exports = checkIfOutdated 59 | -------------------------------------------------------------------------------- /helpers/check-if-outdated.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const checkIfOutdated = require('./check-if-outdated') 4 | const { checkNodeVersion } = checkIfOutdated 5 | 6 | describe('Helper: checkNodeVersion', () => { 7 | beforeEach(() => { 8 | mockProcessProperty('exit', jasmine.createSpy()) 9 | }) 10 | afterEach(() => { 11 | restoreProcessProperty('exit') 12 | restoreProcessProperty('version') 13 | }) 14 | 15 | it('warns and exits with node v0.12', () => { 16 | mockProcessProperty('version', 'v0.12') 17 | const stdout = catchLog(checkNodeVersion) 18 | expect(stdout).toMatch('You must upgrade node') 19 | expect(process.exit).toHaveBeenCalledWith(1) 20 | }) 21 | 22 | it('does not warn or exit with node v4.3.2', () => { 23 | mockProcessProperty('version', 'v4.3.2') 24 | const stdout = catchLog(checkNodeVersion) 25 | expect(stdout).toBe('') 26 | expect(process.exit).not.toHaveBeenCalled() 27 | }) 28 | }) 29 | 30 | describe('Helper: checkMigrationHelperVersion', () => { 31 | const currentVersion = require('../package.json').version 32 | 33 | beforeEach(() => { 34 | mockProcessProperty('exit', jasmine.createSpy()) 35 | }) 36 | afterEach(() => { 37 | restoreProcessProperty('exit') 38 | }) 39 | 40 | it('warns and exits with an old version of vue-migration-helper', done => { 41 | const rewire = require('rewire') 42 | const mod = rewire('./check-if-outdated') 43 | mod.__set__({ 44 | request: (options, callback) => { 45 | callback(null, { statusCode: 200 }, JSON.stringify({ 46 | 'dist-tags': { 47 | 'latest': '999.999.999' 48 | } 49 | })) 50 | } 51 | }) 52 | catchLog(mod.checkMigrationHelperVersion, stdout => { 53 | expect(stdout).toMatch('newer version of vue-migration-helper is available') 54 | expect(stdout).toMatch('latest: 999.999.999') 55 | expect(stdout).toMatch('installed: ' + currentVersion) 56 | expect(process.exit).toHaveBeenCalledWith(1) 57 | done() 58 | }) 59 | }) 60 | 61 | it('does not warn or exit with the latest version of vue-migration-helper', done => { 62 | const rewire = require('rewire') 63 | const mod = rewire('./check-if-outdated') 64 | mod.__set__({ 65 | request: (options, callback) => { 66 | callback(null, { statusCode: 200 }, JSON.stringify({ 67 | 'dist-tags': { 68 | 'latest': currentVersion 69 | } 70 | })) 71 | } 72 | }) 73 | catchLog(mod.checkMigrationHelperVersion, stdout => { 74 | expect(stdout).toBe('') 75 | expect(process.exit).not.toHaveBeenCalled() 76 | done() 77 | }) 78 | }) 79 | 80 | it('does not warn or exit when it cannot reach the NPM registry', done => { 81 | const rewire = require('rewire') 82 | const mod = rewire('./check-if-outdated') 83 | mod.__set__({ 84 | request: (options, callback) => { 85 | callback(null, { statusCode: 404 }) 86 | } 87 | }) 88 | catchLog(mod.checkMigrationHelperVersion, stdout => { 89 | expect(stdout).toBe('') 90 | expect(process.exit).not.toHaveBeenCalled() 91 | done() 92 | }) 93 | }) 94 | }) 95 | 96 | var origProcessValues = {} 97 | function mockProcessProperty (property, value) { 98 | if (typeof origProcessValues[property] === 'undefined') { 99 | origProcessValues[property] = process[property] 100 | } 101 | Object.defineProperty(process, property, { 102 | value: value 103 | }) 104 | } 105 | function restoreProcessProperty (property) { 106 | if (typeof origProcessValues[property] !== 'undefined') { 107 | Object.defineProperty(process, property, { 108 | value: origProcessValues[property] 109 | }) 110 | origProcessValues[property] = undefined 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /helpers/migration-guide-url-for.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (library) { 4 | if (library === 'vue') { 5 | return 'http://vuejs.org/guide/migration.html' 6 | } else { 7 | return 'http://vuejs.org/guide/migration-' + library + '.html' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /helpers/migration-guide-url-for.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const migrationGuideUrlFor = require('./migration-guide-url-for') 4 | 5 | describe('Helper: migration-guide-url-for', () => { 6 | it('returns the main migration guide for vue', () => { 7 | expect( 8 | migrationGuideUrlFor('vue') 9 | ).toBe('http://vuejs.org/guide/migration.html') 10 | }) 11 | 12 | it('returns a suffixed migration guide for companion libraries', () => { 13 | expect( 14 | migrationGuideUrlFor('vue-router') 15 | ).toBe('http://vuejs.org/guide/migration-vue-router.html') 16 | expect( 17 | migrationGuideUrlFor('vuex') 18 | ).toBe('http://vuejs.org/guide/migration-vuex.html') 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /helpers/parent-folder-name-for.js: -------------------------------------------------------------------------------- 1 | module.exports = function (file) { 2 | return file.match(/([^\/\\]+)[\/\\][^\/\\]+\.js/)[1] 3 | } 4 | -------------------------------------------------------------------------------- /helpers/parent-folder-name-for.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const getParentFolderName = require('./parent-folder-name-for') 4 | 5 | describe('Helper: parent-folder-name-for', () => { 6 | it('returns the main migration guide for vue', () => { 7 | expect( 8 | getParentFolderName('/Users/fritzc/code/vue-migration-helper/rules/vue/array-prototype-remove.js') 9 | ).toBe('vue') 10 | }) 11 | 12 | it('returns the main migration guide for vue', () => { 13 | expect( 14 | getParentFolderName('C:\\Users\\fuis\\AppData\\Roaming\\npm\\node_modules\\vue-migration-helper\\rules\\vue\\array-prototype-remove.js') 15 | ).toBe('vue') 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /helpers/print-summary.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | var migrationGuideUrlFor = require('./migration-guide-url-for') 5 | 6 | module.exports = function (deprecationsFound) { 7 | if (!deprecationsFound) { 8 | console.log() 9 | console.log(chalk.green( 10 | 'No obsolete syntax was detected. Well done!' 11 | )) 12 | console.log() 13 | console.log(chalk.yellow( 14 | 'Note however that only about 80% of API changes are detectable with this utility. See the migration guide for the rest:' 15 | )) 16 | console.log() 17 | console.log(chalk.blue.underline(migrationGuideUrlFor('vue'))) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /helpers/print-summary.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const printSummary = require('./print-summary') 4 | 5 | describe('Helper: print-summary', () => { 6 | it('prints nothing when deprecations are found', () => { 7 | const stdout = catchLog(() => { 8 | printSummary(true) 9 | }) 10 | expect(stdout).toBe('') 11 | }) 12 | 13 | it('prints a message when deprecations are not found', () => { 14 | const stdout = catchLog(() => { 15 | printSummary(false) 16 | }) 17 | expect(stdout).toContain('No obsolete syntax was detected.') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /helpers/report-warning.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | var migrationGuideUrlFor = require('./migration-guide-url-for') 5 | var parentFolderNameFor = require('./parent-folder-name-for') 6 | 7 | var warningCount = 0 8 | module.exports = function (fileData, warning, rule) { 9 | warningCount++ 10 | 11 | var library = parentFolderNameFor(rule.file) 12 | 13 | console.log() 14 | console.log(chalk.yellow( 15 | warningCount + '. ' + warning.fix 16 | )) 17 | console.log(chalk.blue(' Line ' + fileData.lineNum + ': ' + fileData.file)) 18 | console.log(chalk.cyan.dim(' Reason: ' + warning.reason)) 19 | console.log(chalk.cyan.dim( 20 | ' More info: ' + 21 | chalk.underline(migrationGuideUrlFor(library) + '#' + warning.docsHash) 22 | )) 23 | } 24 | -------------------------------------------------------------------------------- /helpers/report-warning.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const reportWarning = require('./report-warning') 4 | 5 | describe('Helper: report-warning', () => { 6 | it('prints the correct output for a simple deprecation', () => { 7 | const stdout = catchLog(() => { 8 | reportWarning({ 9 | file: '/foo/bar/baz.js', 10 | line: 42, 11 | lineNum: 'bbb' 12 | }, { 13 | reason: 'ccc', 14 | fix: 'ddd', 15 | docsHash: 'eee' 16 | }, { 17 | file: '/xxx/yyy/zzz.js' 18 | }) 19 | }) 20 | 21 | expect(stdout).toBe(` 22 | 1. ddd 23 | Line bbb: /foo/bar/baz.js 24 | Reason: ccc 25 | More info: http://vuejs.org/guide/migration-yyy.html#eee`) 26 | }) 27 | 28 | it('prints the correct output for a simple vue deprecation', () => { 29 | const stdout = catchLog(() => { 30 | reportWarning({ 31 | file: '/foo/bar/baz.js', 32 | line: 42, 33 | lineNum: 'bbb' 34 | }, { 35 | reason: 'ccc', 36 | fix: 'ddd', 37 | docsHash: 'eee' 38 | }, { 39 | file: '/xxx/vue/zzz.js' 40 | }) 41 | }) 42 | 43 | expect(stdout).toBe(` 44 | 2. ddd 45 | Line bbb: /foo/bar/baz.js 46 | Reason: ccc 47 | More info: http://vuejs.org/guide/migration.html#eee`) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | 4 | require('./helpers/check-if-outdated')(function () { 5 | var fs = require('graceful-fs') 6 | var glob = require('glob') 7 | var split = require('split') 8 | 9 | var checkForDeprecations = require('./helpers/check-for-deprecations') 10 | var printSummary = require('./helpers/print-summary') 11 | 12 | var args = process.argv.slice(2) 13 | var filesAndOrFolders = args.length 14 | ? args.length === 1 15 | ? args + '{/**/*,}' 16 | : '{' + args.join(',') + '}{/**/*,}' 17 | : '**/*' 18 | 19 | glob(filesAndOrFolders, { 20 | nodir: true, 21 | ignore: [ 22 | '**/.git/**/*', 23 | '**/node_modules/**/*', 24 | '**/tmp/**/*', 25 | '**/vendor/**/*', 26 | '**/cache/**/*', 27 | '**/logs/**/*', 28 | '**/dist/**/*', 29 | '**/*.+(jpeg|jpg|gif|png|svg|woff|woff2|ttf|otf|eot|log|zip|map|tar|gz|db|sqlite|sqlite3)', 30 | '**/(G|g)ulpfile.js' 31 | ] 32 | }, function (error, files) { 33 | if (error) throw error 34 | var deprecationsFound = false 35 | var fileChecks = files.map(function (file) { 36 | return new Promise(function (resolve, reject) { 37 | var lineNum = 0 38 | fs.createReadStream(file) 39 | .pipe(split()) 40 | .on('data', function (line) { 41 | lineNum++ 42 | var lineHasDeprecation = checkForDeprecations({ 43 | line: line, 44 | lineNum: lineNum, 45 | file: file 46 | }) 47 | if (lineHasDeprecation) { 48 | deprecationsFound = true 49 | } 50 | }) 51 | .on('end', resolve) 52 | }) 53 | }) 54 | Promise.all(fileChecks) 55 | .then(function () { 56 | printSummary(deprecationsFound) 57 | }) 58 | .catch(function (error) { 59 | throw error 60 | }) 61 | }) 62 | }) 63 | 64 | -------------------------------------------------------------------------------- /jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": ".", 3 | "spec_files": [ 4 | "helpers/**/*.spec.js", 5 | "rules/**/*.spec.js", 6 | "spec/**/*.spec.js" 7 | ], 8 | "helpers": [ 9 | "spec/helpers/**/*.js" 10 | ], 11 | "stopSpecOnExpectationFailure": true 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-migration-helper", 3 | "description": "CLI tool to aid in migration from Vue 1.x to 2.0", 4 | "author": "Chris Fritz", 5 | "license": "MIT", 6 | "version": "1.1.8", 7 | "main": "index.js", 8 | "bin": { 9 | "vue-migration-helper": "index.js" 10 | }, 11 | "scripts": { 12 | "lint": "eslint .", 13 | "test": "JASMINE_CONFIG_PATH=jasmine.json jasmine" 14 | }, 15 | "dependencies": { 16 | "chalk": "^1.1.3", 17 | "glob": "^7.0.6", 18 | "graceful-fs": "^4.1.6", 19 | "lodash.camelcase": "^4.3.0", 20 | "recursive-readdir-sync": "^1.0.6", 21 | "request": "^2.75.0", 22 | "semver": "^5.3.0", 23 | "split": "^1.0.0" 24 | }, 25 | "devDependencies": { 26 | "eslint": "^3.14.1", 27 | "eslint-config-vue": "^2.0.2", 28 | "eslint-plugin-vue": "^2.0.0", 29 | "jasmine": "2.4.1", 30 | "rewire": "^2.5.2", 31 | "strip-ansi": "^3.0.1" 32 | }, 33 | "eslintConfig": { 34 | "extends": "vue", 35 | "globals": { 36 | "jasmine": false, 37 | "describe": false, 38 | "fdescribe": false, 39 | "it": false, 40 | "fit": false, 41 | "expect": false, 42 | "beforeEach": false, 43 | "afterEach": false, 44 | "spyOn": false, 45 | "createRuleChecker": true, 46 | "runMigrationHelper": true, 47 | "catchLog": true 48 | } 49 | }, 50 | "engines": { 51 | "node": ">=4.0.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rules/enforce-warning-type.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const glob = require('glob') 5 | 6 | describe('All rule warnings', () => { 7 | it('have an appropriate type', () => { 8 | const files = glob.sync(path.join(__dirname, '/**/*.js'), { 9 | nodir: true, 10 | ignore: ['**/*.spec.js'] 11 | }) 12 | 13 | files.forEach(file => { 14 | const rule = require(file) 15 | expect(rule.warning('', '', '', '', '').type) 16 | .toMatch(/^(js|template|style|package\.json)$/) 17 | }) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /rules/vue-router/arbitrary-route-properties.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /[\s\.\$](route\s*\.\s*)((?!(?:path|components?|instances|name|parent|redirect|matchAs|beforeEnter|meta|query|params|matched|router|subRoutes|fullPath))\w+)/, 7 | warning: function (match, routeDot, property) { 8 | return { 9 | reason: 'Arbitrary route properties must now be scoped under the new meta property, to avoid conflicts with future features', 10 | fix: ( 11 | 'Replace ' + chalk.red(match.slice(1)) + ' with ' + 12 | chalk.green(routeDot + 'meta.' + property) + 13 | ', then also update the route option to be scoped under meta' 14 | ), 15 | docsHash: 'Arbitrary-Route-Properties', 16 | type: 'js' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rules/vue-router/arbitrary-route-properties.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/arbitrary-route-properties') 4 | 5 | describe('Rule: arbitrary-route-properties', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "route.meta"', () => { 12 | const warning = check('route.meta') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "route.path"', () => { 17 | const warning = check('route.path') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('does not match "route.query"', () => { 22 | const warning = check('route.query') 23 | expect(warning).toBe(null) 24 | }) 25 | 26 | it('does not match "foo-route.bar"', () => { 27 | const warning = check('foo-route.bar') 28 | expect(warning).toBe(null) 29 | }) 30 | 31 | it('does not match "foo-route.bar"', () => { 32 | const warning = check('foo-route.bar') 33 | expect(warning).toBe(null) 34 | }) 35 | 36 | it('does not match "route.redirect"', () => { 37 | const warning = check('route.query') 38 | expect(warning).toBe(null) 39 | }) 40 | 41 | it('matches "route.foo"', () => { 42 | const warning = check(` 43 | route.foo 44 | `) 45 | expect(warning).toBeTruthy() 46 | expect(warning.fix).toBe('Replace route.foo with route.meta.foo, then also update the route option to be scoped under meta') 47 | }) 48 | 49 | it('matches "route.foo.bar.baz"', () => { 50 | const warning = check(` 51 | route.foo.bar.baz 52 | `) 53 | expect(warning).toBeTruthy() 54 | expect(warning.fix).toBe('Replace route.foo with route.meta.foo, then also update the route option to be scoped under meta') 55 | }) 56 | 57 | it('matches "route.foo[1]"', () => { 58 | const warning = check(` 59 | route.foo[1] 60 | `) 61 | expect(warning).toBeTruthy() 62 | expect(warning.fix).toBe('Replace route.foo with route.meta.foo, then also update the route option to be scoped under meta') 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /rules/vue-router/canreuse-false.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bcanReuse\s*?:/, 7 | warning: function (match) { 8 | return { 9 | reason: 'There\'s no longer a use case for canReuse in the new Vue Router', 10 | fix: ( 11 | 'Remove the ' + chalk.red('canReuse') + ' option' 12 | ), 13 | docsHash: 'canReuse-false', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue-router/canreuse-false.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/canreuse-false') 4 | 5 | describe('Rule: canreuse-false', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "can reuse"', () => { 12 | const warning = check('can reuse') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "canReuse"', () => { 17 | const warning = check('canReuse') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "canReuse:"', () => { 22 | const warning = check(` 23 | canReuse: 24 | `) 25 | expect(warning).toBeTruthy() 26 | expect(warning.fix).toBe('Remove the canReuse option') 27 | }) 28 | 29 | it('matches "canReuse :"', () => { 30 | const warning = check(` 31 | canReuse : 32 | `) 33 | expect(warning).toBeTruthy() 34 | expect(warning.fix).toBe('Remove the canReuse option') 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /rules/vue-router/hashbang-false.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bhashbang\s*?:/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Hashbangs are no longer required for Google to crawl a URL, so they are no longer the default (or even an option) for the hash strategy', 10 | fix: ( 11 | 'Remove the ' + chalk.red('hashbang') + ' option' 12 | ), 13 | docsHash: 'hashbang-false', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue-router/hashbang-false.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/hashbang-false') 4 | 5 | describe('Rule: hashbang-false', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "can reuse"', () => { 12 | const warning = check('can reuse') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "hashbang"', () => { 17 | const warning = check('hashbang') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "hashbang:"', () => { 22 | const warning = check(` 23 | hashbang: 24 | `) 25 | expect(warning).toBeTruthy() 26 | expect(warning.fix).toBe('Remove the hashbang option') 27 | }) 28 | 29 | it('matches "hashbang :"', () => { 30 | const warning = check(` 31 | hashbang : 32 | `) 33 | expect(warning).toBeTruthy() 34 | expect(warning.fix).toBe('Remove the hashbang option') 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /rules/vue-router/history-and-abstract-modes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b(history|abstract)(\s*?):(\s*?)true\b/, 7 | warning: function (match, mode, spaceBeforeColon, spaceAfterColon) { 8 | return { 9 | reason: 'All router modes (e.g. history, hash, abstract) are now set through the new mode option', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green('mode' + spaceBeforeColon + ':' + spaceAfterColon + '\'' + mode + '\'') 13 | ), 14 | docsHash: mode + '-true', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue-router/history-and-abstract-modes.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/history-and-abstract-modes') 4 | 5 | describe('Rule: history-and-abstract-modes', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "history"', () => { 12 | const warning = check('history') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('matches "history: true,"', () => { 17 | const warning = check(` 18 | history: true, 19 | `) 20 | expect(warning).toBeTruthy() 21 | expect(warning.fix).toBe('Replace history: true with mode: \'history\'') 22 | }) 23 | 24 | it('matches "history:true,"', () => { 25 | const warning = check(` 26 | history:true, 27 | `) 28 | expect(warning).toBeTruthy() 29 | expect(warning.fix).toBe('Replace history:true with mode:\'history\'') 30 | }) 31 | 32 | it('matches "history : true,"', () => { 33 | const warning = check(` 34 | history : true, 35 | `) 36 | expect(warning).toBeTruthy() 37 | expect(warning.fix).toBe('Replace history : true with mode : \'history\'') 38 | }) 39 | 40 | it('matches "abstract: true,"', () => { 41 | const warning = check(` 42 | abstract: true, 43 | `) 44 | expect(warning).toBeTruthy() 45 | expect(warning.fix).toBe('Replace abstract: true with mode: \'abstract\'') 46 | }) 47 | 48 | it('sets the correct docsHash for history: true', () => { 49 | const warning = check(` 50 | history: true, 51 | `) 52 | expect(warning).toBeTruthy() 53 | expect(warning.docsHash).toBe('history-true') 54 | }) 55 | 56 | it('sets the correct docsHash for abstract: true', () => { 57 | const warning = check(` 58 | abstract: true, 59 | `) 60 | expect(warning).toBeTruthy() 61 | expect(warning.docsHash).toBe('abstract-true') 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /rules/vue-router/loading-route-data.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\$loadingRouteData/, 7 | warning: function (match) { 8 | return { 9 | reason: 'The same results can now be achieved with normal reactive properties, so it\'s been removed', 10 | fix: ( 11 | 'Replace ' + chalk.red('$loadingRouteData') + ' with a reactive property that you define (see the link below for an example)' 12 | ), 13 | docsHash: 'loadingRouteData', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue-router/loading-route-data.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/loading-route-data') 4 | 5 | describe('Rule: loading-route-data', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "loading route data"', () => { 12 | const warning = check('can reuse') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "loadingRouteData"', () => { 17 | const warning = check('loadingRouteData') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "$loadingRouteData"', () => { 22 | const warning = check(` 23 | $loadingRouteData 24 | `) 25 | expect(warning).toBeTruthy() 26 | expect(warning.fix).toBe('Replace $loadingRouteData with a reactive property that you define (see the link below for an example)') 27 | }) 28 | 29 | it('matches "this.$loadingRouteData"', () => { 30 | const warning = check(` 31 | this.$loadingRouteData 32 | `) 33 | expect(warning).toBeTruthy() 34 | expect(warning.fix).toBe('Replace $loadingRouteData with a reactive property that you define (see the link below for an example)') 35 | }) 36 | 37 | it('matches "this.$loadingRouteData" surrounded by quotes', () => { 38 | const warning = check(` 39 | "$loadingRouteData" 40 | `) 41 | expect(warning).toBeTruthy() 42 | expect(warning.fix).toBe('Replace $loadingRouteData with a reactive property that you define (see the link below for an example)') 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /rules/vue-router/root-option.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\broot\s*?:\s*?['"`]\//, 7 | warning: function (match) { 8 | return { 9 | reason: 'Renamed to base for consistency with the HTML element', 10 | fix: ( 11 | 'Rename the ' + chalk.red('root') + ' option to ' + 12 | chalk.green('base') 13 | ), 14 | docsHash: 'root', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue-router/root-option.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/root-option') 4 | 5 | describe('Rule: root-option', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "root"', () => { 12 | const warning = check('root') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "root:"', () => { 17 | const warning = check('root:') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('does not match "root: \'\'"', () => { 22 | const warning = check('root: \'\'') 23 | expect(warning).toBe(null) 24 | }) 25 | 26 | it('does not match "root: \'foo\'"', () => { 27 | const warning = check('root: \'foo\'') 28 | expect(warning).toBe(null) 29 | }) 30 | 31 | it('matches "root: \'/\'"', () => { 32 | const warning = check(` 33 | root: '/' 34 | `) 35 | expect(warning).toBeTruthy() 36 | expect(warning.fix).toBe('Rename the root option to base') 37 | }) 38 | 39 | it('matches "root: "/""', () => { 40 | const warning = check(` 41 | root: "/" 42 | `) 43 | expect(warning).toBeTruthy() 44 | expect(warning.fix).toBe('Rename the root option to base') 45 | }) 46 | 47 | it('matches "root: `/`', () => { 48 | const warning = check(` 49 | root: \`/\` 50 | `) 51 | expect(warning).toBeTruthy() 52 | expect(warning.fix).toBe('Rename the root option to base') 53 | }) 54 | 55 | it('matches "root: \'/foo\'"', () => { 56 | const warning = check(` 57 | root: '/foo' 58 | `) 59 | expect(warning).toBeTruthy() 60 | expect(warning.fix).toBe('Rename the root option to base') 61 | }) 62 | 63 | it('matches "root: \'/foo/bar\'"', () => { 64 | const warning = check(` 65 | root: '/foo/bar' 66 | `) 67 | expect(warning).toBeTruthy() 68 | expect(warning.fix).toBe('Rename the root option to base') 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /rules/vue-router/route-path-one-or-more-named-parameters.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /([`"'][^`"']*?\/)\*([^`"'\/\s]+)([^`"']*?[`"'])/, 7 | warning: function (match, pre, param, post) { 8 | return { 9 | reason: 'The syntax for route matching has changed since Vue Router now uses path-to-regexp under the hood', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green(pre + ':' + param + '+' + post) 13 | ), 14 | docsHash: 'One-or-More-Named-Parameters', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue-router/route-path-one-or-more-named-parameters.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/route-path-one-or-more-named-parameters') 4 | 5 | describe('Rule: route-path-one-or-more-named-parameters', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a JS comment wrapped by quotes', () => { 12 | const warning = check('"/* foo */"') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "/foo/bar/baz"', () => { 17 | const warning = check('"/foo/bar/baz"') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "/*bar"', () => { 22 | const warning = check(` 23 | "/*bar" 24 | `) 25 | expect(warning).toBeTruthy() 26 | expect(warning.fix).toBe('Replace "/*bar" with "/:bar+"') 27 | }) 28 | 29 | it('matches "/foo/*bar"', () => { 30 | const warning = check(` 31 | "/foo/*bar" 32 | `) 33 | expect(warning).toBeTruthy() 34 | expect(warning.fix).toBe('Replace "/foo/*bar" with "/foo/:bar+"') 35 | }) 36 | 37 | it('matches "/foo/*bar/baz"', () => { 38 | const warning = check(` 39 | "/foo/*bar/baz" 40 | `) 41 | expect(warning).toBeTruthy() 42 | expect(warning.fix).toBe('Replace "/foo/*bar/baz" with "/foo/:bar+/baz"') 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /rules/vue-router/router-alias.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b(router\s*?\.\s*?alias)\s*?\(/, 7 | warning: function (match, routerAlias) { 8 | return { 9 | reason: 'Aliases have been moved to an option on route definitions to improve config organization', 10 | fix: ( 11 | 'Replace ' + chalk.red(routerAlias) + ' with ' + 12 | 'an alias option in a route definition (see link below for details)' 13 | ), 14 | docsHash: 'router-alias', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue-router/router-alias.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/router-alias') 4 | 5 | describe('Rule: router-alias', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "router"', () => { 12 | const warning = check('router') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "alias"', () => { 17 | const warning = check('on') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "router.alias("', () => { 22 | const warning = check(` 23 | router.alias( 24 | `) 25 | expect(warning).toBeTruthy() 26 | expect(warning.fix).toBe('Replace router.alias with an alias option in a route definition (see link below for details)') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /rules/vue-router/router-go.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b(router\s*\.\s*)go\s*\(\s*['"`\{]/, 7 | warning: function (match, routerDot) { 8 | return { 9 | reason: 'For consistency with the HTML5 History API, router.go is now only used for back/forward navigation, while router.push is used to navigate to a specific page', 10 | fix: ( 11 | 'Replace ' + chalk.red(routerDot + 'go') + ' with ' + 12 | chalk.green(routerDot + 'push') 13 | ), 14 | docsHash: 'router-go', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue-router/router-go.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/router-go') 4 | 5 | describe('Rule: router-go', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "router"', () => { 12 | const warning = check('router') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "go"', () => { 17 | const warning = check('go') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('does not match "router.go(-2)"', () => { 22 | const warning = check('router.go(-2)') 23 | expect(warning).toBe(null) 24 | }) 25 | 26 | it('does not match "router.go(3)"', () => { 27 | const warning = check('router.go(3)') 28 | expect(warning).toBe(null) 29 | }) 30 | 31 | it('does not match "router.go(foo)"', () => { 32 | const warning = check('router.go(foo)') 33 | expect(warning).toBe(null) 34 | }) 35 | 36 | it('matches "router.go(\'foo\')"', () => { 37 | const warning = check(` 38 | router.go('foo') 39 | `) 40 | expect(warning).toBeTruthy() 41 | expect(warning.fix).toBe('Replace router.go with router.push') 42 | }) 43 | 44 | it('matches "router.go(\'/\')"', () => { 45 | const warning = check(` 46 | router.go('/') 47 | `) 48 | expect(warning).toBeTruthy() 49 | expect(warning.fix).toBe('Replace router.go with router.push') 50 | }) 51 | 52 | it('matches "router.go(\'/\')"', () => { 53 | const warning = check(` 54 | router.go(\` 55 | /foo/bar 56 | \`) 57 | `) 58 | expect(warning).toBeTruthy() 59 | expect(warning.fix).toBe('Replace router.go with router.push') 60 | }) 61 | 62 | it('matches "router.go({ path: \'/\' })"', () => { 63 | const warning = check(` 64 | router.go({ path: '/' }) 65 | `) 66 | expect(warning).toBeTruthy() 67 | expect(warning.fix).toBe('Replace router.go with router.push') 68 | }) 69 | 70 | it('matches multiline "router.go({ path: \'/\' })"', () => { 71 | const warning = check(` 72 | router.go({ 73 | path: '/' 74 | }) 75 | `) 76 | expect(warning).toBeTruthy() 77 | expect(warning.fix).toBe('Replace router.go with router.push') 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /rules/vue-router/router-lifecycle-data.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /^\s*data\s*?(?::\s*\(?\s*[\w,\s]+\)?\s*=>|\(\s*\w+)/, 7 | warning: function (match, hook) { 8 | return { 9 | reason: 'The data route lifecycle hook is no longer necessary now that $route is reactive', 10 | fix: ( 11 | 'Replace the ' + chalk.red('data') + ' route hook with a watcher that reacts to $route changes' 12 | ), 13 | docsHash: 'data', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue-router/router-lifecycle-data.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/router-lifecycle-data') 4 | 5 | describe('Rule: router-lifecycle-data', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match data with ES5 function', () => { 12 | const warning = check(` 13 | data: function () { 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match data with ES5 function and a space before the colon', () => { 19 | const warning = check(` 20 | data : function () { 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match data with ES5 function and a space before the colon, but not after', () => { 26 | const warning = check(` 27 | data :function () { 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('does not match data with ES5 function and no spaces around the colon', () => { 33 | const warning = check(` 34 | data:function () { 35 | `) 36 | expect(warning).toBe(null) 37 | }) 38 | 39 | it('does not match data with ES2015 arrow function', () => { 40 | const warning = check(` 41 | data: () => { 42 | `) 43 | expect(warning).toBe(null) 44 | }) 45 | 46 | it('does not match data with ES2015 object function', () => { 47 | const warning = check(` 48 | data () { 49 | `) 50 | expect(warning).toBe(null) 51 | }) 52 | 53 | it('does not match data with ES2015 object function and no spaces around parentheses', () => { 54 | const warning = check(` 55 | data(){ 56 | `) 57 | expect(warning).toBe(null) 58 | }) 59 | 60 | it('does not match activate in text with parentheses after it', () => { 61 | const warning = check(` 62 | blah blah data (blah blah) 63 | `) 64 | expect(warning).toBe(null) 65 | }) 66 | 67 | it('matches data with ES2015 arrow function and one argument', () => { 68 | const warning = check(` 69 | data: foo => { 70 | `) 71 | expect(warning).toBeTruthy() 72 | expect(warning.fix).toBe('Replace the data route hook with a watcher that reacts to $route changes') 73 | }) 74 | 75 | it('matches data with ES2015 arrow function and one argument with parentheses', () => { 76 | const warning = check(` 77 | data: (foo) => { 78 | `) 79 | expect(warning).toBeTruthy() 80 | expect(warning.fix).toBe('Replace the data route hook with a watcher that reacts to $route changes') 81 | }) 82 | 83 | it('matches data with ES2015 arrow function and multiple arguments with parentheses', () => { 84 | const warning = check(` 85 | data: (foo, bar, baz) => { 86 | `) 87 | expect(warning).toBeTruthy() 88 | expect(warning.fix).toBe('Replace the data route hook with a watcher that reacts to $route changes') 89 | }) 90 | 91 | it('matches data with ES2015 object function and one argument', () => { 92 | const warning = check(` 93 | data (foo) { 94 | `) 95 | expect(warning).toBeTruthy() 96 | expect(warning.fix).toBe('Replace the data route hook with a watcher that reacts to $route changes') 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /rules/vue-router/router-lifecycle-hooks.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | const hookReplacements = { 6 | activate: 'beforeRouteEnter', 7 | canActivate: 'beforeEnter', 8 | deactivate: 'beforeDestroy', 9 | canDeactivate: 'beforeRouteLeave' 10 | } 11 | 12 | const extraInfo = { 13 | activate: 'in the component', 14 | canActivate: 'in the route', 15 | deactivate: '(or ' + chalk.green('destroyed') + ') in the component', 16 | canDeactivate: 'in the component' 17 | } 18 | 19 | module.exports = { 20 | pattern: new RegExp( 21 | '^\\s*(' + 22 | Object.keys(hookReplacements).join('|') + 23 | ')\\s*?(?::|\\()' 24 | ), 25 | warning: function (match, hook) { 26 | const replacementHook = hookReplacements[hook] + '' 27 | const info = extraInfo[hook] 28 | return { 29 | reason: 'The ' + hook + ' router lifecycle hook has been removed', 30 | fix: ( 31 | 'Replace ' + chalk.red(hook) + ' with ' + 32 | (replacementHook.indexOf(' ') === -1 33 | ? chalk.green(replacementHook) 34 | : replacementHook) + ' ' + 35 | (info || '') 36 | ), 37 | docsHash: hook + '', 38 | type: 'js' 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /rules/vue-router/router-lifecycle-hooks.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/router-lifecycle-hooks') 4 | 5 | describe('Rule: router-lifecycle-hooks', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match activated:', () => { 12 | const warning = check('activated:') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match activated:', () => { 17 | const warning = check('activated:') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('does not match data in text with parentheses after it', () => { 22 | const warning = check(` 23 | blah blah activate (blah blah) 24 | `) 25 | expect(warning).toBe(null) 26 | }) 27 | 28 | it('matches activate with ES5 function', () => { 29 | const warning = check(` 30 | activate: function () { 31 | `) 32 | expect(warning).toBeTruthy() 33 | }) 34 | 35 | it('matches activate with ES5 function and a space before the colon', () => { 36 | const warning = check(` 37 | activate : function () { 38 | `) 39 | expect(warning).toBeTruthy() 40 | }) 41 | 42 | it('matches activate with ES5 function and a space before the colon, but not after', () => { 43 | const warning = check(` 44 | activate :function () { 45 | `) 46 | expect(warning).toBeTruthy() 47 | }) 48 | 49 | it('matches activate with ES5 function and no spaces around the colon', () => { 50 | const warning = check(` 51 | activate:function () { 52 | `) 53 | expect(warning).toBeTruthy() 54 | }) 55 | 56 | it('matches activate with ES2015 arrow function', () => { 57 | const warning = check(` 58 | activate: () => { 59 | `) 60 | expect(warning).toBeTruthy() 61 | }) 62 | 63 | it('matches activate with ES2015 arrow function and one argument', () => { 64 | const warning = check(` 65 | activate: foo => { 66 | `) 67 | expect(warning).toBeTruthy() 68 | }) 69 | 70 | it('matches activate with ES2015 object function', () => { 71 | const warning = check(` 72 | activate () { 73 | `) 74 | expect(warning).toBeTruthy() 75 | }) 76 | 77 | it('matches activate with ES2015 object function and no spaces around parentheses', () => { 78 | const warning = check(` 79 | activate(){ 80 | `) 81 | expect(warning).toBeTruthy() 82 | }) 83 | 84 | it('matches activate with ES2015 object function and one argument', () => { 85 | const warning = check(` 86 | activate (foo) { 87 | `) 88 | expect(warning).toBeTruthy() 89 | }) 90 | 91 | it('generates the appropriate fix for activate', () => { 92 | const warning = check(` 93 | activate () { 94 | `) 95 | expect(warning).toBeTruthy() 96 | expect(warning.fix).toBe('Replace activate with beforeRouteEnter in the component') 97 | }) 98 | 99 | it('generates the appropriate fix for canActivate', () => { 100 | const warning = check(` 101 | canActivate () { 102 | `) 103 | expect(warning).toBeTruthy() 104 | expect(warning.fix).toBe('Replace canActivate with beforeEnter in the route') 105 | }) 106 | 107 | it('generates the appropriate fix for deactivate', () => { 108 | const warning = check(` 109 | deactivate () { 110 | `) 111 | expect(warning).toBeTruthy() 112 | expect(warning.fix).toBe('Replace deactivate with beforeDestroy (or destroyed) in the component') 113 | }) 114 | 115 | it('generates the appropriate fix for canDeactivate', () => { 116 | const warning = check(` 117 | canDeactivate () { 118 | `) 119 | expect(warning).toBeTruthy() 120 | expect(warning.fix).toBe('Replace canDeactivate with beforeRouteLeave in the component') 121 | }) 122 | }) 123 | -------------------------------------------------------------------------------- /rules/vue-router/router-map.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b(router\s*?\.\s*?map)\s*?\(/, 7 | warning: function (match, routerMap) { 8 | return { 9 | reason: 'Routes are now defined as an array on a routes option at router instantiation', 10 | fix: ( 11 | 'Replace ' + chalk.red(routerMap) + ' with ' + 12 | 'an array on the new routes option (see link below for details)' 13 | ), 14 | docsHash: 'router-map', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue-router/router-map.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/router-map') 4 | 5 | describe('Rule: router-map', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "router"', () => { 12 | const warning = check('router') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "map"', () => { 17 | const warning = check('map') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "router.map("', () => { 22 | const warning = check(` 23 | router.map( 24 | `) 25 | expect(warning).toBeTruthy() 26 | expect(warning.fix).toBe('Replace router.map with an array on the new routes option (see link below for details)') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /rules/vue-router/router-on.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b(router\s*?\.\s*?on)\s*?\(/, 7 | warning: function (match, routerOn) { 8 | return { 9 | reason: 'It\'s now recommended to just modify the routes defined on a router instance dynamically', 10 | fix: ( 11 | 'Replace ' + chalk.red(routerOn) + ' with ' + 12 | 'a route that\'s dynamically added to the routes array (see link below for details)' 13 | ), 14 | docsHash: 'router-on', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue-router/router-on.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/router-on') 4 | 5 | describe('Rule: router-on', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "router"', () => { 12 | const warning = check('router') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "on"', () => { 17 | const warning = check('on') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "router.on("', () => { 22 | const warning = check(` 23 | router.on( 24 | `) 25 | expect(warning).toBeTruthy() 26 | expect(warning.fix).toBe('Replace router.on with a route that\'s dynamically added to the routes array (see link below for details)') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /rules/vue-router/router-redirect.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b(router\s*?\.\s*?redirect)\s*?\(/, 7 | warning: function (match, routerRedirect) { 8 | return { 9 | reason: 'Redirects have been moved to an option on route definitions to improve config organization', 10 | fix: ( 11 | 'Replace ' + chalk.red(routerRedirect) + ' with ' + 12 | 'a redirect option in a route definition (see link below for details)' 13 | ), 14 | docsHash: 'router-redirect', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue-router/router-redirect.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/router-redirect') 4 | 5 | describe('Rule: router-redirect', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "router"', () => { 12 | const warning = check('router') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "redirect"', () => { 17 | const warning = check('on') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "router.redirect("', () => { 22 | const warning = check(` 23 | router.redirect( 24 | `) 25 | expect(warning).toBeTruthy() 26 | expect(warning.fix).toBe('Replace router.redirect with a redirect option in a route definition (see link below for details)') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /rules/vue-router/router-start.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b(router\s*?\.\s*?start)\s*?\(/, 7 | warning: function (match, routerStart) { 8 | return { 9 | reason: 'Starting an app with routing no longer requires a special method - the router can simply be passed to the root Vue instance as option', 10 | fix: ( 11 | 'Replace ' + chalk.red(routerStart) + ' with ' + 12 | chalk.green('router: router') + 13 | ' on your root Vue instance (see link below for details)' 14 | ), 15 | docsHash: 'router-start', 16 | type: 'js' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rules/vue-router/router-start.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/router-start') 4 | 5 | describe('Rule: router-start', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "router"', () => { 12 | const warning = check('router') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "start"', () => { 17 | const warning = check('on') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "router.start("', () => { 22 | const warning = check(` 23 | router.start( 24 | `) 25 | expect(warning).toBeTruthy() 26 | expect(warning.fix).toBe('Replace router.start with router: router on your root Vue instance (see link below for details)') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /rules/vue-router/save-scroll-position.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bsaveScrollPosition\b/, 7 | warning: function (match) { 8 | return { 9 | reason: 'The binary nature of saveScrollPosition wasn\'t sufficient for many use cases, but the new scrollBehavior option allows scroll behavior to be fully customized', 10 | fix: ( 11 | 'Replace ' + chalk.red('saveScrollPosition') + ' with the new ' + 12 | chalk.green('scrollBehavior') + ' option, which takes a function (see the link below for examples)' 13 | ), 14 | docsHash: 'saveScrollPosition', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue-router/save-scroll-position.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/save-scroll-position') 4 | 5 | describe('Rule: save-scroll-position', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "save scroll position"', () => { 12 | const warning = check('save scroll position') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('matches "saveScrollPosition"', () => { 17 | const warning = check('saveScrollPosition') 18 | expect(warning).toBeTruthy() 19 | expect(warning.fix).toBe('Replace saveScrollPosition with the new scrollBehavior option, which takes a function (see the link below for examples)') 20 | }) 21 | 22 | it('matches "saveScrollPosition:"', () => { 23 | const warning = check(` 24 | saveScrollPosition: 25 | `) 26 | expect(warning).toBeTruthy() 27 | expect(warning.fix).toBe('Replace saveScrollPosition with the new scrollBehavior option, which takes a function (see the link below for examples)') 28 | }) 29 | 30 | it('matches "saveScrollPosition :"', () => { 31 | const warning = check(` 32 | saveScrollPosition : 33 | `) 34 | expect(warning).toBeTruthy() 35 | expect(warning.fix).toBe('Replace saveScrollPosition with the new scrollBehavior option, which takes a function (see the link below for examples)') 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /rules/vue-router/subroutes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bsubRoutes\b/, 7 | warning: function (match) { 8 | return { 9 | reason: 'subRoutes has been renamed to children for consistency within Vue and with other routing libraries', 10 | fix: ( 11 | 'Replace ' + chalk.red('subRoutes') + ' with ' + 12 | chalk.green('children') + 13 | ' and update its routes to the new array syntax' 14 | ), 15 | docsHash: 'subRoutes', 16 | type: 'js' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rules/vue-router/subroutes.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/subroutes') 4 | 5 | describe('Rule: subroutes', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "routes"', () => { 12 | const warning = check('routes') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('matches "subRoutes"', () => { 17 | const warning = check('subRoutes') 18 | expect(warning).toBeTruthy() 19 | expect(warning.fix).toBe('Replace subRoutes with children and update its routes to the new array syntax') 20 | }) 21 | 22 | it('matches "subRoutes:"', () => { 23 | const warning = check(` 24 | subRoutes: 25 | `) 26 | expect(warning).toBeTruthy() 27 | expect(warning.fix).toBe('Replace subRoutes with children and update its routes to the new array syntax') 28 | }) 29 | 30 | it('matches "subRoutes :"', () => { 31 | const warning = check(` 32 | subRoutes : 33 | `) 34 | expect(warning).toBeTruthy() 35 | expect(warning.fix).toBe('Replace subRoutes with children and update its routes to the new array syntax') 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /rules/vue-router/suppress-transition-error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bsuppressTransitionError\b/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Removed due to hooks simplification - if you really must suppress transition errors, you can use JavaScript\'s try-catch instead', 10 | fix: ( 11 | 'Remove the ' + chalk.red('suppressTransitionError') + ' option' 12 | ), 13 | docsHash: 'suppressTransitionError', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue-router/suppress-transition-error.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/suppress-transition-error') 4 | 5 | describe('Rule: suppress-transition-error', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "suppress transition error"', () => { 12 | const warning = check('suppress transition error') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "suppresstransitionerror"', () => { 17 | const warning = check('suppresstransitionerror') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "suppressTransitionError"', () => { 22 | const warning = check('suppressTransitionError') 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Remove the suppressTransitionError option') 25 | }) 26 | 27 | it('matches "suppressTransitionError:"', () => { 28 | const warning = check(` 29 | suppressTransitionError: 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Remove the suppressTransitionError option') 33 | }) 34 | 35 | it('matches "suppressTransitionError :"', () => { 36 | const warning = check(` 37 | suppressTransitionError : 38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Remove the suppressTransitionError option') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /rules/vue-router/transition-on-load.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\btransitionOnLoad\b/, 7 | warning: function (match) { 8 | return { 9 | reason: 'This option is no longer necessary now that Vue\'s transition system has explicit appear transition control', 10 | fix: ( 11 | 'Remove the ' + chalk.red('transitionOnLoad') + ' option' 12 | ), 13 | docsHash: 'transitionOnLoad', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue-router/transition-on-load.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/transition-on-load') 4 | 5 | describe('Rule: transition-on-load', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "transition"', () => { 12 | const warning = check('routes') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match "transition on load"', () => { 17 | const warning = check('routes') 18 | expect(warning).toBe(null) 19 | }) 20 | 21 | it('matches "transitionOnLoad"', () => { 22 | const warning = check('transitionOnLoad') 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Remove the transitionOnLoad option') 25 | }) 26 | 27 | it('matches "transitionOnLoad:"', () => { 28 | const warning = check(` 29 | transitionOnLoad: 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Remove the transitionOnLoad option') 33 | }) 34 | 35 | it('matches "transitionOnLoad :"', () => { 36 | const warning = check(` 37 | transitionOnLoad : 38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Remove the transitionOnLoad option') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /rules/vue-router/v-link-active.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bv-link-active\b/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Active route classes are now handled through the component', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with the new component with a custom tag (see link below for details)' 12 | ), 13 | docsHash: 'v-link-active', 14 | type: 'template' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue-router/v-link-active.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/v-link-active') 4 | 5 | describe('Rule: v-link-active', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple v-link-active', () => { 12 | const warning = check(` 13 |
  • 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace v-link-active with the new component with a custom tag (see link below for details)') 17 | }) 18 | 19 | it('matches a v-link-active with other attributes', () => { 20 | const warning = check(` 21 |
  • 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace v-link-active with the new component with a custom tag (see link below for details)') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue-router/v-link.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bv-link=(?:'[^']+'|"[^"]+")/, 7 | warning: function (match) { 8 | return { 9 | reason: 'The v-link directive has been replaced with the new component', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with the new component (see link below for details)' 12 | ), 13 | docsHash: 'v-link', 14 | type: 'template' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue-router/v-link.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/v-link') 4 | 5 | describe('Rule: v-link', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple v-link', () => { 12 | const warning = check(` 13 | 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace v-link="\'/foo\'" with the new component (see link below for details)') 17 | }) 18 | 19 | it('matches simple v-link with object', () => { 20 | const warning = check(` 21 | 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace v-link="{ path: \'/foo\' }" with the new component (see link below for details)') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue-router/vue-router-dependency.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /("vue-router"\s*:\s*)"[^\d"]*?[^2]\.\d+\.\d+"/, 7 | warning: function (match, preVersion) { 8 | return { 9 | reason: 'If you are using pre-2.0 Vue Router through NPM, you have to update it in your package.json file', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green( 13 | preVersion + '"^2.0.0"' 14 | ) + 15 | ', then run: npm install' 16 | ), 17 | docsHash: '', 18 | type: 'package.json' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vue-router/vue-router-dependency.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue-router/vue-router-dependency') 4 | 5 | describe('Rule: vue-router-dependency', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "^2.0.0"', () => { 12 | const warning = check(` 13 | "vue-router": "^2.0.0" 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match "^2.0.0" with comma', () => { 19 | const warning = check(` 20 | "vue-router": "^2.0.0", 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match "^2.0.1"', () => { 26 | const warning = check(` 27 | "vue-router": "^2.0.1" 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('does not match "2.0.0"', () => { 33 | const warning = check(` 34 | "vue-router": "2.0.0" 35 | `) 36 | expect(warning).toBe(null) 37 | }) 38 | 39 | it('does not match vuex "1.0.0"', () => { 40 | const warning = check(` 41 | "vuex": "1.0.0" 42 | `) 43 | expect(warning).toBe(null) 44 | }) 45 | 46 | it('matches "^0.7.13"', () => { 47 | const warning = check(` 48 | "vue-router": "^0.7.13" 49 | `) 50 | expect(warning).toBeTruthy() 51 | expect(warning.fix).toBe('Replace "vue-router": "^0.7.13" with "vue-router": "^2.0.0", then run: npm install') 52 | }) 53 | 54 | it('matches "^0.7.13" with comma', () => { 55 | const warning = check(` 56 | "vue-router": "^0.7.13", 57 | `) 58 | expect(warning).toBeTruthy() 59 | expect(warning.fix).toBe('Replace "vue-router": "^0.7.13" with "vue-router": "^2.0.0", then run: npm install') 60 | }) 61 | 62 | it('matches "0.7.13"', () => { 63 | const warning = check(` 64 | "vue-router": "0.7.13", 65 | `) 66 | expect(warning).toBeTruthy() 67 | expect(warning.fix).toBe('Replace "vue-router": "0.7.13" with "vue-router": "^2.0.0", then run: npm install') 68 | }) 69 | 70 | it('matches "0.7.0"', () => { 71 | const warning = check(` 72 | "vue-router": "0.7.0", 73 | `) 74 | expect(warning).toBeTruthy() 75 | expect(warning.fix).toBe('Replace "vue-router": "0.7.0" with "vue-router": "^2.0.0", then run: npm install') 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /rules/vue/array-prototype-remove.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /([\w+\.]+?)\.\$remove\s*?\(([\w\.]+?)\)/, 7 | warning: function (match, array, itemToRemove) { 8 | return { 9 | reason: 'Array extensions for the reactivity system have been removed', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green( 13 | 'var index = ' + array + '.indexOf(' + itemToRemove.trim() + '); ' + 14 | array + '.splice(index, 1)' 15 | ) 16 | ), 17 | docsHash: 'Array-prototype-remove', 18 | type: 'js' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vue/array-prototype-remove.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/array-prototype-remove') 4 | 5 | describe('Rule: array-prototype-remove', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match this.foo.$remove()', () => { 12 | const warning = check(` 13 | this.foo.$remove() 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match foo.$remove()', () => { 19 | const warning = check(` 20 | foo.$remove() 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match foo.$remove(function () {})', () => { 26 | const warning = check(` 27 | foo.$remove(function () {}) 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('does not match foo.$remove(() => {}))', () => { 33 | const warning = check(` 34 | foo.$remove(() => {})) 35 | `) 36 | expect(warning).toBe(null) 37 | }) 38 | 39 | it('does not match foo.$remove(() => bar))', () => { 40 | const warning = check(` 41 | foo.$remove(() => bar)) 42 | `) 43 | expect(warning).toBe(null) 44 | }) 45 | 46 | it('does not match foo.$remove(bar => bar))', () => { 47 | const warning = check(` 48 | foo.$remove(bar => bar)) 49 | `) 50 | expect(warning).toBe(null) 51 | }) 52 | 53 | it('matches a simple array remove', () => { 54 | const warning = check(` 55 | this.items.$remove(item) 56 | `) 57 | expect(warning).toBeTruthy() 58 | expect(warning.fix).toBe('Replace this.items.$remove(item) with var index = this.items.indexOf(item); this.items.splice(index, 1)') 59 | }) 60 | 61 | it('matches a simple array remove with independent variable', () => { 62 | const warning = check(` 63 | items.$remove(item) 64 | `) 65 | expect(warning).toBeTruthy() 66 | expect(warning.fix).toBe('Replace items.$remove(item) with var index = items.indexOf(item); items.splice(index, 1)') 67 | }) 68 | }) 69 | -------------------------------------------------------------------------------- /rules/vue/array-prototype-set.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /([\w+\.]+?)\.\$set\s*?\(([\w\s\.\[\]]+?),([^,]+?)\)/, 7 | warning: function (match, array, indexArg, newValueArg) { 8 | return { 9 | reason: 'Array extensions for the reactivity system have been removed', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green( 13 | 'Vue.set(' + 14 | array + ', ' + 15 | indexArg.trim() + ', ' + 16 | newValueArg.trim() + 17 | ')' 18 | ) 19 | ), 20 | docsHash: 'Array-prototype-set', 21 | type: 'js' 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /rules/vue/array-prototype-set.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/array-prototype-set') 4 | 5 | describe('Rule: array-prototype-set', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a typical object set', () => { 12 | const warning = check(` 13 | this.$set(this.album, 'likeCount', data.result.likeCount) 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match vm.$set(\'a.b\', 2)', () => { 19 | const warning = check(` 20 | vm.$set('a.b', 2) 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('matches a simple array set', () => { 26 | const warning = check(` 27 | this.array.$set(42, 'foo') 28 | `) 29 | expect(warning).toBeTruthy() 30 | expect(warning.fix).toBe('Replace this.array.$set(42, \'foo\') with Vue.set(this.array, 42, \'foo\')') 31 | }) 32 | 33 | it('matches a simple array set with var instead of literal number', () => { 34 | const warning = check(` 35 | this.array.$set(num, 'foo') 36 | `) 37 | expect(warning).toBeTruthy() 38 | expect(warning.fix).toBe('Replace this.array.$set(num, \'foo\') with Vue.set(this.array, num, \'foo\')') 39 | }) 40 | 41 | it('matches a simple array set with independent variable', () => { 42 | const warning = check(` 43 | array.$set(42, 'foo') 44 | `) 45 | expect(warning).toBeTruthy() 46 | expect(warning.fix).toBe('Replace array.$set(42, \'foo\') with Vue.set(array, 42, \'foo\')') 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /rules/vue/built-in-filters.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | const builtInFilters = { 6 | capitalize: 'Replace the built-in $red{$filter} filter with a custom filter, method, or computed property using $green{npmjs.com/package/lodash.capitalize}', 7 | uppercase: 'Replace $red{$property | $filter} with $green{$property.toUpperCase()}', 8 | lowercase: 'Replace $red{$property | $filter} with $green{$property.toLowerCase()}', 9 | currency: 'Replace the built-in $red{$filter} filter with a custom filter, method, or computed property using $green{format} from $green{npmjs.com/package/accounting}', 10 | pluralize: 'Replace the built-in $red{$filter} filter with a custom filter, method, or computed property using $green{npmjs.com/package/pluralize}', 11 | json: 'Remove the built-in $red{$filter} filter, as output is now automatically nicely formatted' 12 | } 13 | 14 | module.exports = { 15 | pattern: new RegExp( 16 | '(\\w+)\\s*?[^\\|]\\|\\s*?(' + 17 | Object.keys(builtInFilters).join('|') + 18 | ')' 19 | ), 20 | warning: function (match, property, filter) { 21 | const message = builtInFilters[filter] + '' 22 | return { 23 | reason: 'Built-in filters have been removed in favor of external, specialized utility libraries', 24 | fix: message 25 | .replace(/\$property/g, property) 26 | .replace(/\$filter/g, filter) 27 | .replace(/\$green\{(.+?)\}/g, chalk.green('$1')) 28 | .replace(/\$red\{(.+?)\}/g, chalk.red('$1')), 29 | docsHash: 'Replacing-the-' + filter + '-Filter', 30 | type: 'template' 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rules/vue/built-in-filters.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/built-in-filters') 4 | 5 | describe('Rule: built-in-filters', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple capitalize filter', () => { 12 | const warning = check(` 13 | {{ hi | capitalize }} 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace the built-in capitalize filter with a custom filter, method, or computed property using npmjs.com/package/lodash.capitalize') 17 | }) 18 | 19 | it('matches a simple uppercase filter', () => { 20 | const warning = check(` 21 | {{ hi | uppercase }} 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace hi | uppercase with hi.toUpperCase()') 25 | }) 26 | 27 | it('matches a simple lowercase filter', () => { 28 | const warning = check(` 29 | {{ hi | lowercase }} 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace hi | lowercase with hi.toLowerCase()') 33 | }) 34 | 35 | it('matches a simple currency filter', () => { 36 | const warning = check(` 37 | {{ hi | currency }} 38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Replace the built-in currency filter with a custom filter, method, or computed property using format from npmjs.com/package/accounting') 41 | }) 42 | 43 | it('matches a simple pluralize filter', () => { 44 | const warning = check(` 45 | {{ hi | pluralize }} 46 | `) 47 | expect(warning).toBeTruthy() 48 | expect(warning.fix).toBe('Replace the built-in pluralize filter with a custom filter, method, or computed property using npmjs.com/package/pluralize') 49 | }) 50 | 51 | it('matches a simple json filter', () => { 52 | const warning = check(` 53 | {{ hi | json }} 54 | `) 55 | expect(warning).toBeTruthy() 56 | expect(warning.fix).toBe('Remove the built-in json filter, as output is now automatically nicely formatted') 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /rules/vue/cache-false.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bcache\s*?:\s*?false\b/, 7 | warning: function (match) { 8 | return { 9 | reason: 'The cache option on computed properties has been deprecated, as it\'s better to simply use a method instead', 10 | fix: 'Refactor the computed property into a method', 11 | docsHash: 'cache-false', 12 | type: 'js' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rules/vue/cache-false.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/cache-false') 4 | 5 | describe('Rule: cache-false', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "cache"', () => { 12 | const warning = check('cache') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('matches "cache: false,"', () => { 17 | const warning = check(` 18 | cache: false, 19 | `) 20 | expect(warning).toBeTruthy() 21 | expect(warning.fix).toBe('Refactor the computed property into a method') 22 | }) 23 | 24 | it('matches "cache:false,"', () => { 25 | const warning = check(` 26 | cache:false, 27 | `) 28 | expect(warning).toBeTruthy() 29 | expect(warning.fix).toBe('Refactor the computed property into a method') 30 | }) 31 | 32 | it('matches "cache: false,"', () => { 33 | const warning = check(` 34 | cache : false, 35 | `) 36 | expect(warning).toBeTruthy() 37 | expect(warning.fix).toBe('Refactor the computed property into a method') 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /rules/vue/data-assignment.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /[\w+\.]+?\.\$data\s*?=[^=][^\n]+/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Replacing $data is no longer allowed, as it often has unintended side effects', 10 | fix: ( 11 | 'Instead of replacing $data with ' + chalk.red(match) + 12 | ', update individual properties or scope all the properties you want to update under a new object property, then replace that object' 13 | ), 14 | docsHash: 'Replacing-vm-data', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue/data-assignment.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/data-assignment') 4 | 5 | describe('Rule: data-assignment', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not vm.$data === data', () => { 12 | const warning = check(` 13 | vm.$data === data 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches a simple data assignment', () => { 19 | const warning = check(` 20 | this.$data = { a: 'b' } 21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Instead of replacing $data with this.$data = { a: \'b\' }, update individual properties or scope all the properties you want to update under a new object property, then replace that object') 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /rules/vue/debounce-param.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bdebounce=['"]?\d+['"]?/, 7 | warning: function (match) { 8 | return { 9 | reason: 'debounce was quite limited and reinvented a utility function better maintained in libraries such as lodash and underscore', 10 | fix: ( 11 | 'Delete ' + chalk.red(match) + ' and wrap the expensive operation(s) you want to throttle with lodash\'s debounce or throttle functions (see the link below for an example)' 12 | ), 13 | docsHash: 'v-model-with-debounce', 14 | type: 'template' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/debounce-param.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/debounce-param') 4 | 5 | describe('Rule: debounce-param', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple debounce param', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Delete debounce="100" and wrap the expensive operation(s) you want to throttle with lodash\'s debounce or throttle functions (see the link below for an example)') 17 | }) 18 | 19 | it('matches a debounce param without quotes', () => { 20 | const warning = check(` 21 |

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Delete debounce=100 and wrap the expensive operation(s) you want to throttle with lodash\'s debounce or throttle functions (see the link below for an example)') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue/directive-filters.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\s((?:v-|@|:)[\w\.:-]+=(?:"[^"\|]+?\|[^\|][^"]+?"|'[^'\|]+?\|[^\|][^']+?'))/, 7 | warning: function (match, directive) { 8 | return { 9 | reason: 'Filters can now only be used inside text interpolations, e.g. {{ date | formatDate(\'YY-MM-DD\') }}', 10 | fix: ( 11 | 'Replace filtered value in ' + chalk.red(directive) + ' with a method or computed property' 12 | ), 13 | docsHash: 'Filters-Outside-Text-Interpolations', 14 | type: 'template' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/directive-filters.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/directive-filters') 4 | 5 | describe('Rule: directive-filters', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a simple v-bind', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match a v-bind with || (or)', () => { 19 | const warning = check(` 20 |

    21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('matches a simple v-model filter', () => { 26 | const warning = check(` 27 |

    28 | `) 29 | expect(warning).toBeTruthy() 30 | expect(warning.fix).toBe('Replace filtered value in v-model="foo | bar" with a method or computed property') 31 | }) 32 | 33 | it('matches a simple v-on filter', () => { 34 | const warning = check(` 35 |

    36 | `) 37 | expect(warning).toBeTruthy() 38 | expect(warning.fix).toBe('Replace filtered value in v-on:keyup.enter="foo | bar" with a method or computed property') 39 | }) 40 | 41 | it('matches a simple v-on shorthand filter', () => { 42 | const warning = check(` 43 |

    44 | `) 45 | expect(warning).toBeTruthy() 46 | expect(warning.fix).toBe('Replace filtered value in @keyup.enter="foo | bar" with a method or computed property') 47 | }) 48 | 49 | it('matches a simple v-bind shorthand filter', () => { 50 | const warning = check(` 51 |

    52 | `) 53 | expect(warning).toBeTruthy() 54 | expect(warning.fix).toBe('Replace filtered value in :foo="bar | baz" with a method or computed property') 55 | }) 56 | 57 | it('matches a v-for filter with an argument', () => { 58 | const warning = check(` 59 |

    60 | `) 61 | expect(warning).toBeTruthy() 62 | expect(warning.fix).toBe('Replace filtered value in v-for="item in items | formatDate \'YY-MM-DD\'" with a method or computed property') 63 | }) 64 | 65 | it('matches chained v-for filters', () => { 66 | const warning = check(` 67 |

    68 | `) 69 | expect(warning).toBeTruthy() 70 | expect(warning.fix).toBe('Replace filtered value in v-for="item in items | foo bar | baz bez" with a method or computed property') 71 | }) 72 | }) 73 | -------------------------------------------------------------------------------- /rules/vue/directive-literal-modifier.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b(v-[\w-]+)([\w-\.:]*?)\.literal([\w-\.]*?)="(.+?)"/, 7 | warning: function (match, name, preLiteralStuff, postLiteralStuff, value) { 8 | return { 9 | reason: 'Directives have been vastly simplified and no longer include the literal modifier', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green(name + preLiteralStuff + postLiteralStuff + '="\'' + value + '\'"') 13 | ), 14 | docsHash: 'Directive-literal-Modifier', 15 | type: 'template' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue/directive-literal-modifier.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/directive-literal-modifier') 4 | 5 | describe('Rule: directive-literal-modifier', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a normal directive in an element', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match a normal directive standalone', () => { 19 | const warning = check(` 20 | v-if="foo" 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('matches simple directive with literal modifier in element', () => { 26 | const warning = check(` 27 |

    28 | `) 29 | expect(warning).toBeTruthy() 30 | expect(warning.fix).toBe('Replace v-my-directive.literal="foo" with v-my-directive="\'foo\'"') 31 | }) 32 | 33 | it('matches simple directive with literal modifier standalone', () => { 34 | const warning = check(` 35 | v-my-directive.literal="foo" 36 | `) 37 | expect(warning).toBeTruthy() 38 | expect(warning.fix).toBe('Replace v-my-directive.literal="foo" with v-my-directive="\'foo\'"') 39 | }) 40 | 41 | it('matches complex directive with literal modifier standalone', () => { 42 | const warning = check(` 43 | v-my-directive:foo.a.literal.b="foo" 44 | `) 45 | expect(warning).toBeTruthy() 46 | expect(warning.fix).toBe('Replace v-my-directive:foo.a.literal.b="foo" with v-my-directive:foo.a.b="\'foo\'"') 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /rules/vue/dispatch-and-broadcast.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(\w+)\.\$(?:dispatch|broadcast)\s*?\(.+?\)/, 7 | warning: function (match) { 8 | return { 9 | reason: '$dispatch and $broadcast have been removed because the pattern doesn\'t scale well', 10 | fix: 'Replace ' + chalk.red(match) + ' to use a global event bus or vuex (see link below for implementation details)', 11 | docsHash: 'dispatch-and-broadcast', 12 | type: 'js' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rules/vue/dispatch-and-broadcast.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/dispatch-and-broadcast') 4 | 5 | describe('Rule: dispatch-and-broadcast', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches this.$dispatch(\'foo\')', () => { 12 | const warning = check(` 13 | this.$dispatch('foo') 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace this.$dispatch(\'foo\') to use a global event bus or vuex (see link below for implementation details)') 17 | }) 18 | 19 | it('matches this.$broadcast(\'foo\')', () => { 20 | const warning = check(` 21 | this.$broadcast('foo') 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace this.$broadcast(\'foo\') to use a global event bus or vuex (see link below for implementation details)') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue/filter-arguments-without-parentheses.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\{\{\s*(?:(?!\}\}).)+?\|[^\|]\s*((\w+)\s+((?:(?!\}\})(?!\(.+?\))(?!\|).)+))\s*(?:(?!\}\}).)+?\}\}/, 7 | warning: function (match, filter, filterName, filterArgs) { 8 | return { 9 | reason: 'Filters with arguments must now use the same syntax as JavaScript functions', 10 | fix: ( 11 | 'Replace ' + chalk.red(filter.trim()) + ' with ' + 12 | chalk.green( 13 | filterName + '(' + 14 | filterArgs.trim().split(/\s+/).join(', ') + 15 | ')' 16 | ) 17 | ), 18 | docsHash: 'Filter-Argument-Syntax', 19 | type: 'template' 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /rules/vue/filter-arguments-without-parentheses.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/filter-arguments-without-parentheses') 4 | 5 | describe('Rule: filter-arguments-without-parentheses', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a filter with a single argument and the correct syntax', () => { 12 | const warning = check(` 13 |

    {{ foo | formatDate('YY/MM/DD') }}

    14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches a filter with a single argument', () => { 19 | const warning = check(` 20 |

    {{ foo | formatDate 'YY/MM/DD' }}

    21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Replace formatDate \'YY/MM/DD\' with formatDate(\'YY/MM/DD\')') 24 | }) 25 | 26 | it('matches a filter with a multiple arguments', () => { 27 | const warning = check(` 28 |

    {{ foo | formatDate 'YY/MM/DD' foo 'bar' baz }}

    29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Replace formatDate \'YY/MM/DD\' foo \'bar\' baz with formatDate(\'YY/MM/DD\', foo, \'bar\', baz)') 32 | }) 33 | 34 | it('matches a filter with a multiple arguments and other filters chained after it', () => { 35 | const warning = check(` 36 |

    {{ foo | formatDate 'YY/MM/DD' foo 'bar' baz | anotherFilter hi ho hi ho }}

    37 | `) 38 | expect(warning).toBeTruthy() 39 | expect(warning.fix).toBe('Replace formatDate \'YY/MM/DD\' foo \'bar\' baz with formatDate(\'YY/MM/DD\', foo, \'bar\', baz)') 40 | }) 41 | 42 | it('matches a filter with a multiple arguments and a fixed filter chained after it', () => { 43 | const warning = check(` 44 |

    {{ foo | formatDate 'YY/MM/DD' foo 'bar' baz | anotherFilter(hi, ho, hi, ho) }}

    45 | `) 46 | expect(warning).toBeTruthy() 47 | expect(warning.fix).toBe('Replace formatDate \'YY/MM/DD\' foo \'bar\' baz with formatDate(\'YY/MM/DD\', foo, \'bar\', baz)') 48 | }) 49 | 50 | it('matches a filter with a multiple arguments and a fixed filter chained before it', () => { 51 | const warning = check(` 52 |

    {{ foo | formatDate('YY/MM/DD' foo 'bar' baz) | anotherFilter hi ho hi ho }}

    53 | `) 54 | expect(warning).toBeTruthy() 55 | expect(warning.fix).toBe('Replace anotherFilter hi ho hi ho with anotherFilter(hi, ho, hi, ho)') 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /rules/vue/html-interpolation.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\{\{\{\s*?(.+?)\s*?\}\}\}/, 7 | warning: function (match, interpolationContents) { 8 | return { 9 | reason: 'HTML interpolation with {{{ }}} has been removed', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green('v-html="' + interpolationContents.trim() + '"') + 13 | ' on a containing element' 14 | ), 15 | docsHash: 'HTML-Interpolation', 16 | type: 'template' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rules/vue/html-interpolation.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/html-interpolation') 4 | 5 | describe('Rule: html-interpolation', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple interpolation', () => { 12 | const warning = check(` 13 |

    {{{ foo }}}

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace {{{ foo }}} with v-html="foo" on a containing element') 17 | }) 18 | 19 | it('matches a standalone interpolation', () => { 20 | const warning = check(` 21 | {{{ foo }}} 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace {{{ foo }}} with v-html="foo" on a containing element') 25 | }) 26 | 27 | it('matches an interpolation with two on the same line', () => { 28 | const warning = check(` 29 | {{{ foo }}} bar {{{ baz }}} 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace {{{ foo }}} with v-html="foo" on a containing element') 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /rules/vue/interpolation-within-attributes-no-quotes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b([\w-:@\.]+)=\{\{(.+?)\}\}/, 7 | warning: function (match, attribute, value) { 8 | return { 9 | reason: 'Interpolation within attributes has been removed', 10 | fix: ( 11 | 'Update ' + chalk.red(match) + ' to ' + 12 | chalk.green( 13 | 'v-bind:' + attribute + '="' + 14 | value.trim() + 15 | '"' 16 | ) 17 | ), 18 | docsHash: 'Interpolation-within-Attributes', 19 | type: 'template' 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /rules/vue/interpolation-within-attributes-no-quotes.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/interpolation-within-attributes-no-quotes') 4 | 5 | describe('Rule: interpolation-within-attributes-no-quotes', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a simple interpolation with quotes', () => { 12 | const warning = check(` 13 |
    14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match a more complex interpolation with quotes', () => { 19 | const warning = check(` 20 | 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match a more complex attribute with a more complex interpolation with quotes', () => { 26 | const warning = check(` 27 | 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('does not match interpolation with a string prefix with quotes', () => { 33 | const warning = check(` 34 | 35 | `) 36 | expect(warning).toBe(null) 37 | }) 38 | 39 | it('does not match interpolation with a string suffix with quotes', () => { 40 | const warning = check(` 41 | 42 | `) 43 | expect(warning).toBe(null) 44 | }) 45 | 46 | it('does not match interpolation with both a string prefix and string suffix with quotes', () => { 47 | const warning = check(` 48 | 49 | `) 50 | expect(warning).toBe(null) 51 | }) 52 | 53 | it('does not match multiple interpolations with quotes', () => { 54 | const warning = check(` 55 | 56 | `) 57 | expect(warning).toBe(null) 58 | }) 59 | 60 | it('matches interpolation without quotes', () => { 61 | const warning = check(` 62 | 63 | `) 64 | expect(warning).toBeTruthy() 65 | expect(warning.fix).toBe('Update target={{bar}} to v-bind:target="bar"') 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /rules/vue/interpolation-within-attributes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\b([\w-:@\.]+)="([^"=]*?)\{\{(.+?)\}\}([^"=]*?)"/, 7 | warning: function (match, attribute, prefixString, value, suffixString) { 8 | return { 9 | reason: 'Interpolation within attributes has been removed', 10 | fix: suffixString.indexOf('{{') === -1 && suffixString.indexOf('}}') === -1 11 | ? ( 12 | 'Update ' + chalk.red(match) + ' to ' + 13 | chalk.green( 14 | 'v-bind:' + attribute + '="' + 15 | (prefixString.length 16 | ? '\'' + prefixString.replace('\'', '\\\'') + '\' + ' 17 | : '') + 18 | value.trim() + 19 | (suffixString.length 20 | ? ' + \'' + suffixString.replace('\'', '\\\'') + '\'' 21 | : '') + 22 | '"' 23 | ) 24 | ) 25 | : ( 26 | 'Update ' + chalk.red(match) + ' to ' + 27 | 'use v-bind with a computed property' 28 | ), 29 | docsHash: 'Interpolation-within-Attributes', 30 | type: 'template' 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rules/vue/interpolation-within-attributes.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/interpolation-within-attributes') 4 | 5 | describe('Rule: interpolation-within-attributes', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match interpolations without quotes', () => { 12 | const warning = check(` 13 | 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches a simple interpolation', () => { 19 | const warning = check(` 20 | 21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Update href="{{ url }}" to v-bind:href="url"') 24 | }) 25 | 26 | it('matches a more complex interpolation', () => { 27 | const warning = check(` 28 | 29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Update href="{{ url + \'foo\' + bar }}" to v-bind:href="url + \'foo\' + bar"') 32 | }) 33 | 34 | it('matches a more complex attribute with a more complex interpolation', () => { 35 | const warning = check(` 36 | 37 | `) 38 | expect(warning).toBeTruthy() 39 | expect(warning.fix).toBe('Update data-foo1-6="{{ url + \'foo\' + bar }}" to v-bind:data-foo1-6="url + \'foo\' + bar"') 40 | }) 41 | 42 | it('matches interpolation with a string prefix', () => { 43 | const warning = check(` 44 | 45 | `) 46 | expect(warning).toBeTruthy() 47 | expect(warning.fix).toBe('Update class="btn-{{ type }}" to v-bind:class="\'btn-\' + type"') 48 | }) 49 | 50 | it('matches interpolation with a string suffix', () => { 51 | const warning = check(` 52 | 53 | `) 54 | expect(warning).toBeTruthy() 55 | expect(warning.fix).toBe('Update class="{{ type }}-center" to v-bind:class="type + \'-center\'"') 56 | }) 57 | 58 | it('matches interpolation with both a string prefix and string suffix', () => { 59 | const warning = check(` 60 | 61 | `) 62 | expect(warning).toBeTruthy() 63 | expect(warning.fix).toBe('Update class="foo-{{ bar }}-baz" to v-bind:class="\'foo-\' + bar + \'-baz\'"') 64 | }) 65 | 66 | it('matches multiple interpolations', () => { 67 | const warning = check(` 68 | 69 | `) 70 | expect(warning).toBeTruthy() 71 | expect(warning.fix).toBe('Update class="btn-{{ type }} btn-{{ size }}" to use v-bind with a computed property') 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /rules/vue/keep-alive-attribute.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /<[\w-]+\s+.*?\bkeep-alive\b.*?>/, 7 | warning: function (match) { 8 | return { 9 | reason: 'keep-alive is now a wrapper element, rather than an attribute', 10 | fix: ( 11 | 'Refactor ' + chalk.red('keep-alive') + ' attribute ' + 12 | 'to a ' + chalk.green('') + ' wrapper component' 13 | ), 14 | docsHash: 'keep-alive-Attribute', 15 | type: 'template' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue/keep-alive-attribute.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/keep-alive-attribute') 4 | 5 | describe('Rule: keep-alive-attribute', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple keep-alive attribute', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Refactor keep-alive attribute to a wrapper component') 17 | }) 18 | 19 | it('matches a keep-alive attribute surrounded by complex attributes', () => { 20 | const warning = check(` 21 |

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Refactor keep-alive attribute to a wrapper component') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue/lazy-and-number-params.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /<[\w-]+\s+.*?(v-model[\.\w-:]*)=["'\b](.+?)["'\b].*\s(lazy|number)[\s>].*?>?/, 7 | warning: function (match, vModelAttr, vModelValue, param) { 8 | return { 9 | reason: 'v-model attribute params have been removed in favor of simpler modifier syntax', 10 | fix: ( 11 | 'Refactor ' + chalk.red(param) + ' to ' + 12 | 'a v-model modifier: ' + 13 | chalk.green( 14 | vModelAttr + '.' + param + '="' + vModelValue + '"' 15 | ) 16 | ), 17 | docsHash: 'v-model-with-lazy-or-number-Param-Attributes', 18 | type: 'template' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vue/lazy-and-number-params.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/lazy-and-number-params') 4 | 5 | describe('Rule: lazy-and-number-params', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match v-model.number="foo"', () => { 12 | const warning = check(` 13 | v-model.number="foo" 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match an input with type="number"', () => { 19 | const warning = check(` 20 | 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match number at the end of the value of an attribute', () => { 26 | const warning = check(` 27 | 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('matches a simple number param', () => { 33 | const warning = check(` 34 | 35 | `) 36 | expect(warning).toBeTruthy() 37 | expect(warning.fix).toBe('Refactor number to a v-model modifier: v-model.number="foo"') 38 | }) 39 | 40 | it('matches a simple lazy param', () => { 41 | const warning = check(` 42 | 43 | `) 44 | expect(warning).toBeTruthy() 45 | expect(warning.fix).toBe('Refactor lazy to a v-model modifier: v-model.lazy="foo"') 46 | }) 47 | 48 | it('matches a number param with extra attributes', () => { 49 | const warning = check(` 50 | 51 | `) 52 | expect(warning).toBeTruthy() 53 | expect(warning.fix).toBe('Refactor number to a v-model modifier: v-model.number="foo"') 54 | }) 55 | 56 | it('matches a number param with pre-existing v-model modifier', () => { 57 | const warning = check(` 58 | 59 | `) 60 | expect(warning).toBeTruthy() 61 | expect(warning.fix).toBe('Refactor number to a v-model modifier: v-model.lazy.number="foo"') 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /rules/vue/lifecycle-hooks.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | const hookReplacements = { 6 | beforeCompile: 'created', 7 | compiled: 'mounted', 8 | init: 'beforeCreate', 9 | attached: 'a custom in-DOM check in another hook', 10 | detached: 'a custom in-DOM check in another hook', 11 | ready: 'mounted' 12 | } 13 | 14 | const extraInfo = { 15 | ready: ', then use ' + chalk.green('Vue.nextTick') + ' if you need an in-document guarantee' 16 | } 17 | 18 | module.exports = { 19 | pattern: new RegExp( 20 | '^\\s*(' + 21 | Object.keys(hookReplacements).join('|') + 22 | ')\\s*?(?::|\\()' 23 | ), 24 | warning: function (match, hook) { 25 | const replacementHook = hookReplacements[hook] + '' 26 | const info = extraInfo[hook] 27 | return { 28 | reason: hook + ' lifecycle hook has been removed', 29 | fix: ( 30 | 'Replace ' + chalk.red(hook) + ' with ' + 31 | (replacementHook.indexOf(' ') === -1 32 | ? chalk.green(replacementHook) 33 | : replacementHook) + 34 | (info || '') 35 | ), 36 | docsHash: hook + '', 37 | type: 'js' 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /rules/vue/lifecycle-hooks.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/lifecycle-hooks') 4 | 5 | describe('Rule: lifecycle-hooks', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "already"', () => { 12 | const warning = check('already:') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('does not match ready in text with parentheses after it', () => { 17 | const warning = check(` 18 | blah blah ready (blah blah) 19 | `) 20 | expect(warning).toBe(null) 21 | }) 22 | 23 | it('does not match a normal init function declaration', () => { 24 | const warning = check(` 25 | function init() { 26 | `) 27 | expect(warning).toBe(null) 28 | }) 29 | 30 | it('matches beforeCompile with ES5 function', () => { 31 | const warning = check(` 32 | beforeCompile: function () { 33 | `) 34 | expect(warning).toBeTruthy() 35 | }) 36 | 37 | it('matches beforeCompile with ES5 function and a space before the colon', () => { 38 | const warning = check(` 39 | beforeCompile : function () { 40 | `) 41 | expect(warning).toBeTruthy() 42 | }) 43 | 44 | it('matches beforeCompile with ES5 function and a space before the colon, but not after', () => { 45 | const warning = check(` 46 | beforeCompile :function () { 47 | `) 48 | expect(warning).toBeTruthy() 49 | }) 50 | 51 | it('matches beforeCompile with ES5 function and no spaces around the colon', () => { 52 | const warning = check(` 53 | beforeCompile:function () { 54 | `) 55 | expect(warning).toBeTruthy() 56 | }) 57 | 58 | it('matches beforeCompile with ES2015 arrow function', () => { 59 | const warning = check(` 60 | beforeCompile: () => { 61 | `) 62 | expect(warning).toBeTruthy() 63 | }) 64 | 65 | it('matches beforeCompile with ES2015 arrow function and one argument', () => { 66 | const warning = check(` 67 | beforeCompile: foo => { 68 | `) 69 | expect(warning).toBeTruthy() 70 | }) 71 | 72 | it('matches beforeCompile with ES2015 object function', () => { 73 | const warning = check(` 74 | beforeCompile () { 75 | `) 76 | expect(warning).toBeTruthy() 77 | }) 78 | 79 | it('matches beforeCompile with ES2015 object function and no spaces around parentheses', () => { 80 | const warning = check(` 81 | beforeCompile(){ 82 | `) 83 | expect(warning).toBeTruthy() 84 | }) 85 | 86 | it('matches beforeCompile with ES2015 object function and one argument', () => { 87 | const warning = check(` 88 | beforeCompile (foo) { 89 | `) 90 | expect(warning).toBeTruthy() 91 | }) 92 | 93 | it('generates the appropriate fix for beforeCompile', () => { 94 | const warning = check(` 95 | beforeCompile () { 96 | `) 97 | expect(warning).toBeTruthy() 98 | expect(warning.fix).toBe('Replace beforeCompile with created') 99 | }) 100 | 101 | it('generates the appropriate fix for compiled', () => { 102 | const warning = check(` 103 | compiled () { 104 | `) 105 | expect(warning).toBeTruthy() 106 | expect(warning.fix).toBe('Replace compiled with mounted') 107 | }) 108 | 109 | it('generates the appropriate fix for init', () => { 110 | const warning = check(` 111 | init () { 112 | `) 113 | expect(warning).toBeTruthy() 114 | expect(warning.fix).toBe('Replace init with beforeCreate') 115 | }) 116 | 117 | it('generates the appropriate fix for attached', () => { 118 | const warning = check(` 119 | attached () { 120 | `) 121 | expect(warning).toBeTruthy() 122 | expect(warning.fix).toBe('Replace attached with a custom in-DOM check in another hook') 123 | }) 124 | 125 | it('generates the appropriate fix for detached', () => { 126 | const warning = check(` 127 | detached () { 128 | `) 129 | expect(warning).toBeTruthy() 130 | expect(warning.fix).toBe('Replace detached with a custom in-DOM check in another hook') 131 | }) 132 | 133 | it('generates the appropriate fix for ready', () => { 134 | const warning = check(` 135 | ready () { 136 | `) 137 | expect(warning).toBeTruthy() 138 | expect(warning.fix).toBe('Replace ready with mounted, then use Vue.nextTick if you need an in-document guarantee') 139 | }) 140 | }) 141 | -------------------------------------------------------------------------------- /rules/vue/named-slot-styling.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /.*\[\s*slot\s*=.+?\][^\{]*/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Content inserted via named no longer preserves the slot attribute', 10 | fix: 'Replace ' + chalk.red(match.trim()) + ' with a styled wrapper element or, in advanced use cases, modify the inserted content programmatically using a render function', 11 | docsHash: 'slot-Attribute-Styling', 12 | type: 'template' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rules/vue/named-slot-styling.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/named-slot-styling') 4 | 5 | describe('Rule: named-slot-styling', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a named slot selector', () => { 12 | const warning = check(` 13 | [slot="my-slot"] { 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace [slot="my-slot"] with a styled wrapper element or, in advanced use cases, modify the inserted content programmatically using a render function') 17 | }) 18 | 19 | it('matches a more complex named slot selector', () => { 20 | const warning = check(` 21 | .parent > .child[slot='my-slot'] + .sibling { 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace .parent > .child[slot=\'my-slot\'] + .sibling with a styled wrapper element or, in advanced use cases, modify the inserted content programmatically using a render function') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue/one-time-binding.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\{\{\s*?\*(.+?)\}\}/, 7 | warning: function (match, binding) { 8 | return { 9 | reason: 'One-way bindings have been replaced by a new v-once directive', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green('v-once="' + binding.trim() + '"') + 13 | ' on a containing element' 14 | ), 15 | docsHash: 'One-Time-Bindings', 16 | type: 'template' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rules/vue/one-time-binding.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/one-time-binding') 4 | 5 | describe('Rule: one-time-binding', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple one-time binding', () => { 12 | const warning = check(` 13 |

    {{* foo }}

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace {{* foo }} with v-once="foo" on a containing element') 17 | }) 18 | 19 | it('matches a simple one-time binding with a space before the star', () => { 20 | const warning = check(` 21 |

    {{ * foo }}

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace {{ * foo }} with v-once="foo" on a containing element') 25 | }) 26 | 27 | it('matches a complex one-time binding', () => { 28 | const warning = check(` 29 |

    {{* 2 * 3 / 5 + 11 + foo + bar + 'baz' - 42 }}

    30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace {{* 2 * 3 / 5 + 11 + foo + bar + \'baz\' - 42 }} with v-once="2 * 3 / 5 + 11 + foo + bar + \'baz\' - 42" on a containing element') 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /rules/vue/prop-coerce.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | pattern: /coerce\s*?(?::|\()/, 5 | warning: function (match) { 6 | return { 7 | reason: 'Prop coercion has been removed in favor of local computed properties', 8 | fix: 'Replace prop coercion with a local computed property', 9 | docsHash: 'coerce-Prop-Option', 10 | type: 'js' 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /rules/vue/prop-coerce.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/prop-coerce') 4 | 5 | describe('Rule: prop-coerce', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches coerce with ES5 function', () => { 12 | const warning = check(` 13 | coerce: function () { 14 | `) 15 | expect(warning).toBeTruthy() 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /rules/vue/replace-false.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\breplace\s*?:\s*?false\b/, 7 | warning: function (match) { 8 | return { 9 | reason: 'The replace option has been removed, as it provides too little convenience', 10 | fix: ( 11 | 'Delete the ' + chalk.red(match) + ' option and instead wrap your root component with an element similar to the one you\'re replacing, e.g. ' + 12 | chalk.green('el: \'#app\'') + 13 | ' with ' + 14 | chalk.green('template: \'
    ...
    \'') 15 | ), 16 | docsHash: 'replace-false', 17 | type: 'js' 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rules/vue/replace-false.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/replace-false') 4 | 5 | describe('Rule: replace-false', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "replace"', () => { 12 | const warning = check('replace') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('matches "replace: false,"', () => { 17 | const warning = check(` 18 | replace: false, 19 | `) 20 | expect(warning).toBeTruthy() 21 | expect(warning.fix).toBe('Delete the replace: false option and instead wrap your root component with an element similar to the one you\'re replacing, e.g. el: \'#app\' with template: \'
    ...
    \'') 22 | }) 23 | 24 | it('matches "replace:false,"', () => { 25 | const warning = check(` 26 | replace:false, 27 | `) 28 | expect(warning).toBeTruthy() 29 | expect(warning.fix).toBe('Delete the replace:false option and instead wrap your root component with an element similar to the one you\'re replacing, e.g. el: \'#app\' with template: \'
    ...
    \'') 30 | }) 31 | 32 | it('matches "replace : false,"', () => { 33 | const warning = check(` 34 | replace : false, 35 | `) 36 | expect(warning).toBeTruthy() 37 | expect(warning.fix).toBe('Delete the replace : false option and instead wrap your root component with an element similar to the one you\'re replacing, e.g. el: \'#app\' with template: \'
    ...
    \'') 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /rules/vue/style-with-inline-important.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(?:v-bind)?:style=["']\s*?\{.+?!important.*?\}\s*?["']/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Bound styles no longer support inline !important', 10 | fix: ( 11 | 'Remove !important from ' + chalk.red(match) 12 | ), 13 | docsHash: 'v-bind-style-with-Object-Syntax-and-important', 14 | type: 'template' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/style-with-inline-important.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/style-with-inline-important') 4 | 5 | describe('Rule: style-with-inline-important', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple element with bound styles', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Remove !important from v-bind:style="{ foo: bar !important; }"') 17 | }) 18 | 19 | it('matches a complex element with bound styles', () => { 20 | const warning = check(` 21 |

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Remove !important from v-bind:style="{ foo: bar !important; }"') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue/track-by.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /<[^>]+\b(?:v-for=["']\(?(\w+)[^>]+?["'][^>]+?\b(track-by=["']([\w\.]+)["'])|(track-by=["']([\w\.]+)["'])[^>]+?v-for=["']\(?(\w+)[^>]+?["'])[^>]*?>/, 7 | warning: function (match, vForItemBefore, trackByAfter, trackByValueAfter, trackByBefore, trackByValueBefore, vForItemAfter) { 8 | const vForItem = vForItemBefore || vForItemAfter 9 | const trackBy = trackByBefore || trackByAfter 10 | const trackByValue = trackByValueBefore || trackByValueAfter 11 | return { 12 | reason: 'track-by has been replaced by the key attribute, which conforms with language used by other libraries and behaves the same as other attributes', 13 | fix: ( 14 | 'Update ' + chalk.red(trackBy) + ' to ' + 15 | chalk.green( 16 | 'v-bind:key="' + 17 | vForItem + '.' + trackByValue + 18 | '"' 19 | ) 20 | ), 21 | docsHash: 'track-by', 22 | type: 'template' 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /rules/vue/track-by.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/track-by') 4 | 5 | describe('Rule: track-by', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple element with v-for and track-by, with v-for first', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Update track-by="id" to v-bind:key="item.id"') 17 | }) 18 | 19 | it('matches a simple element with v-for and track-by, with track-by first', () => { 20 | const warning = check(` 21 |

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Update track-by="id" to v-bind:key="item.id"') 25 | }) 26 | 27 | it('matches a simple element with v-for with an index and track-by', () => { 28 | const warning = check(` 29 |

    30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Update track-by="id" to v-bind:key="item.id"') 33 | }) 34 | 35 | it('matches a complex element with v-for with an index and track-by', () => { 36 | const warning = check(` 37 |

    38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Update track-by="id" to v-bind:key="item.id"') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /rules/vue/transition.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /<[\w-]+[^>]*?\s(transition(?:=["']([\w-]+)["'])?)[^>]*?>/, 7 | warning: function (match, attribute, name) { 8 | const nameAttribute = name 9 | ? ' name="' + name + '"' 10 | : '' 11 | return { 12 | reason: 'The new and improved transition system requires use of new and components', 13 | fix: ( 14 | 'Replace ' + chalk.red(attribute) + ' attribute with either a ' + 15 | chalk.green('') + ' or ' + 16 | chalk.green('') + 17 | ' wrapper component' 18 | ), 19 | docsHash: 'transition-Attribute', 20 | type: 'template' 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rules/vue/transition.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/transition') 4 | 5 | describe('Rule: transition', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a transition element', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match a transition element with a name that has "transition" in it', () => { 19 | const warning = check(` 20 | 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match a CSS transition property', () => { 26 | const warning = check(` 27 | transition: all 1s; 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('does not match "

    It\'s also a good idea to explicitly add `v-bind:css="false"` for JavaScript-only transitions so that Vue can skip the CSS detection. This also prevents CSS rules from accidentally interfering with the transition.

    "', () => { 33 | const warning = check(` 34 |

    It's also a good idea to explicitly add \`v-bind:css="false"\` for JavaScript-only transitions so that Vue can skip the CSS detection. This also prevents CSS rules from accidentally interfering with the transition.

    35 | `) 36 | expect(warning).toBe(null) 37 | }) 38 | 39 | it('matches an empty transition attribute', () => { 40 | const warning = check(` 41 |

    foo

    42 | `) 43 | expect(warning).toBeTruthy() 44 | expect(warning.fix).toBe('Replace transition attribute with either a or wrapper component') 45 | }) 46 | 47 | it('matches a empty transition attribute', () => { 48 | const warning = check(` 49 |

    foo

    50 | `) 51 | expect(warning).toBeTruthy() 52 | expect(warning.fix).toBe('Replace transition="fade" attribute with either a or wrapper component') 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /rules/vue/twoway-true.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\btwoWay\s*?:\s*?true\b/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Two-way prop binding has been removed in favor of more explicit event-driven communication between parent and child', 10 | fix: ( 11 | 'Delete ' + chalk.red(match) + ', then $emit an event from the child component to trigger an update to the prop in the parent' 12 | ), 13 | docsHash: 'twoWay-Prop-Option', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/twoway-true.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/twoway-true') 4 | 5 | describe('Rule: twoway-true', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "twoWay"', () => { 12 | const warning = check('twoWay') 13 | expect(warning).toBe(null) 14 | }) 15 | 16 | it('matches "twoWay: true,"', () => { 17 | const warning = check(` 18 | twoWay: true, 19 | `) 20 | expect(warning).toBeTruthy() 21 | expect(warning.fix).toBe('Delete twoWay: true, then $emit an event from the child component to trigger an update to the prop in the parent') 22 | }) 23 | 24 | it('matches "twoWay:true,"', () => { 25 | const warning = check(` 26 | twoWay:true, 27 | `) 28 | expect(warning).toBeTruthy() 29 | expect(warning.fix).toBe('Delete twoWay:true, then $emit an event from the child component to trigger an update to the prop in the parent') 30 | }) 31 | 32 | it('matches "twoWay : true,"', () => { 33 | const warning = check(` 34 | twoWay : true, 35 | `) 36 | expect(warning).toBeTruthy() 37 | expect(warning.fix).toBe('Delete twoWay : true, then $emit an event from the child component to trigger an update to the prop in the parent') 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /rules/vue/v-bind-once.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /((?:v-bind)?:)([\w-]+)\.(once)=(["][^"]+["]|['][^']+[']|\w+)/, 7 | warning: function (match, vBindPrefix, boundProp, modifier, value) { 8 | return { 9 | reason: 'v-bind.sync and v-bind.once have removed to enforce one-way down props, leaving side effects to more explicit component events', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green(vBindPrefix + boundProp + '=' + value) + 13 | ', then make ' + boundProp + ' the initial value of a data property' 14 | ), 15 | docsHash: 'v-bind-with-once-and-sync-Modifiers', 16 | type: 'template' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rules/vue/v-bind-once.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-bind-once') 4 | 5 | describe('Rule: v-bind-once', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple v-bind.once', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace v-bind:foo.once="bar" with v-bind:foo="bar", then make foo the initial value of a data property') 17 | }) 18 | 19 | it('matches a simple v-bind.once without quotes', () => { 20 | const warning = check(` 21 |

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace v-bind:foo.once=bar with v-bind:foo=bar, then make foo the initial value of a data property') 25 | }) 26 | 27 | it('matches a simple v-bind.once with v-bind shorthand', () => { 28 | const warning = check(` 29 |

    30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace :foo.once="bar" with :foo="bar", then make foo the initial value of a data property') 33 | }) 34 | 35 | it('matches a very complex v-bind.once example', () => { 36 | const warning = check(` 37 |

    38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Replace :foo-bar.once=baz with :foo-bar=baz, then make foo-bar the initial value of a data property') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /rules/vue/v-bind-sync.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /((?:v-bind)?:)([\w-]+)\.(sync)=(["][^"]+["]|['][^']+[']|\w+)/, 7 | warning: function (match, vBindPrefix, boundProp, modifier, value) { 8 | return { 9 | reason: 'v-bind.sync and v-bind.once have removed to enforce one-way down props, leaving side effects to more explicit component events', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green(vBindPrefix + boundProp + '=' + value) + 13 | ', then $emit an event from the child component to trigger an update to ' + value.replace(/['"]/g, '') + ' in the parent' 14 | ), 15 | docsHash: 'v-bind-with-once-and-sync-Modifiers', 16 | type: 'template' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rules/vue/v-bind-sync.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-bind-sync') 4 | 5 | describe('Rule: v-bind-sync', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple v-bind.sync', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace v-bind:foo.sync="bar" with v-bind:foo="bar", then $emit an event from the child component to trigger an update to bar in the parent') 17 | }) 18 | 19 | it('matches a simple v-bind.sync without quotes', () => { 20 | const warning = check(` 21 |

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace v-bind:foo.sync=bar with v-bind:foo=bar, then $emit an event from the child component to trigger an update to bar in the parent') 25 | }) 26 | 27 | it('matches a simple v-bind.sync with v-bind shorthand', () => { 28 | const warning = check(` 29 |

    30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace :foo.sync="bar" with :foo="bar", then $emit an event from the child component to trigger an update to bar in the parent') 33 | }) 34 | 35 | it('matches a very complex v-bind.sync example', () => { 36 | const warning = check(` 37 |

    38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Replace :foo-bar.sync=baz with :foo-bar=baz, then $emit an event from the child component to trigger an update to baz in the parent') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /rules/vue/v-el-and-v-ref.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | var camelCase = require('lodash.camelcase') 5 | 6 | module.exports = { 7 | pattern: /\b(v-el|v-ref):([\w-]+)/, 8 | warning: function (match, type, name) { 9 | return { 10 | reason: 'v-el and v-ref merged into ref attribute', 11 | fix: ( 12 | 'Update ' + chalk.red(match) + ' to ' + 13 | chalk.green('ref="' + camelCase(name) + '"') 14 | ), 15 | docsHash: 'v-el-and-v-ref', 16 | type: 'template' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rules/vue/v-el-and-v-ref.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-el-and-v-ref') 4 | 5 | describe('Rule: v-el-and-v-ref', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple v-el', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Update v-el:foo to ref="foo"') 17 | }) 18 | 19 | it('matches a simple v-ref', () => { 20 | const warning = check(` 21 |

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Update v-ref:foo to ref="foo"') 25 | }) 26 | 27 | it('matches v-el with dashes and camelCases it', () => { 28 | const warning = check(` 29 |

    30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Update v-ref:foo-bar to ref="fooBar"') 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /rules/vue/v-el-els.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(\w+)\.\$els(\.\w+|\[.+?\])?/, 7 | warning: function (match, vm, el) { 8 | return { 9 | reason: 'v-el and v-ref merged into ref attribute', 10 | fix: ( 11 | 'Update ' + chalk.red(match) + ' to ' + 12 | chalk.green(vm + '.$refs' + (el || '')) 13 | ), 14 | docsHash: 'v-el-and-v-ref', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue/v-el-els.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-el-els') 4 | 5 | describe('Rule: v-el-els', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches this.$els', () => { 12 | const warning = check(` 13 | this.$els 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Update this.$els to this.$refs') 17 | }) 18 | 19 | it('matches vm.$els', () => { 20 | const warning = check(` 21 | vm.$els 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Update vm.$els to vm.$refs') 25 | }) 26 | 27 | it('matches vm.$els.fooBar', () => { 28 | const warning = check(` 29 | vm.$els.fooBar 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Update vm.$els.fooBar to vm.$refs.fooBar') 33 | }) 34 | 35 | it('matches vm.$els.fooBar with assignment', () => { 36 | const warning = check(` 37 | vm.$els.fooBar = 'baz' 38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Update vm.$els.fooBar to vm.$refs.fooBar') 41 | }) 42 | 43 | it('matches vm.$els[\'fooBar\']', () => { 44 | const warning = check(` 45 | vm.$els['fooBar'] 46 | `) 47 | expect(warning).toBeTruthy() 48 | expect(warning.fix).toBe('Update vm.$els[\'fooBar\'] to vm.$refs[\'fooBar\']') 49 | }) 50 | 51 | it('matches vm.$els[\'fooBar\'] with assignment', () => { 52 | const warning = check(` 53 | vm.$els['fooBar'] = 'baz' 54 | `) 55 | expect(warning).toBeTruthy() 56 | expect(warning.fix).toBe('Update vm.$els[\'fooBar\'] to vm.$refs[\'fooBar\']') 57 | }) 58 | 59 | it('matches vm.$els[fooBar] with assignment', () => { 60 | const warning = check(` 61 | vm.$els[fooBar] = 'baz' 62 | `) 63 | expect(warning).toBeTruthy() 64 | expect(warning.fix).toBe('Update vm.$els[fooBar] to vm.$refs[fooBar]') 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /rules/vue/v-for-argument-order-with-object.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bv-for="\((key)\s*?,\s*?(\w+)\).+?"/, 7 | warning: function (match, keyVar, valueVar) { 8 | return { 9 | reason: 'Argument order for v-for has been updated to match JavaScript conventions', 10 | fix: ( 11 | 'Switch argument order in ' + chalk.red(match) + ' to ' + 12 | chalk.green('(' + valueVar + ', ' + keyVar + ')') 13 | ), 14 | docsHash: 'v-for-Argument-Order-for-Objects', 15 | type: 'template' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue/v-for-argument-order-with-object.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-for-argument-order-with-object') 4 | 5 | describe('Rule: v-for-argument-order-with-object', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a v-for the correct argument order', () => { 12 | const warning = check(` 13 |

    {{ value }}, {{ key }}

    14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match a v-for without a key', () => { 19 | const warning = check(` 20 |

    {{ value }}

    21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('matches a v-for key as the first property', () => { 26 | const warning = check(` 27 |

    {{ value }}, {{ key }}

    28 | `) 29 | expect(warning).toBeTruthy() 30 | expect(warning.fix).toBe('Switch argument order in v-for="(key, value) in object" to (value, key)') 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /rules/vue/v-for-argument-order.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bv-for="\(([ijk]|in?d?e?x?)\s*?,\s*?(\w+)\).+?"/, 7 | warning: function (match, indexVar, itemVar) { 8 | return { 9 | reason: 'Argument order for v-for has been updated to match JavaScript conventions', 10 | fix: ( 11 | 'Switch argument order in ' + chalk.red(match) + ' to ' + 12 | chalk.green('(' + itemVar + ', ' + indexVar + ')') 13 | ), 14 | docsHash: 'v-for-Argument-Order-for-Arrays', 15 | type: 'template' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue/v-for-argument-order.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-for-argument-order') 4 | 5 | describe('Rule: v-for-argument-order', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a v-for the correct argument order', () => { 12 | const warning = check(` 13 |

    {{ item }}, {{ index }}

    14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches a v-for with index as first argument', () => { 19 | const warning = check(` 20 |

    {{ item }}, {{ index }}

    21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Switch argument order in v-for="(index, item) in items" to (item, index)') 24 | }) 25 | 26 | it('matches a v-for with i as first argument', () => { 27 | const warning = check(` 28 |

    {{ item }}, {{ i }}

    29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Switch argument order in v-for="(i, item) in items" to (item, i)') 32 | }) 33 | 34 | it('matches a v-for with j as first argument', () => { 35 | const warning = check(` 36 |

    {{ item }}, {{ j }}

    37 | `) 38 | expect(warning).toBeTruthy() 39 | expect(warning.fix).toBe('Switch argument order in v-for="(j, item) in items" to (item, j)') 40 | }) 41 | 42 | it('matches a v-for with k as first argument', () => { 43 | const warning = check(` 44 |

    {{ item }}, {{ k }}

    45 | `) 46 | expect(warning).toBeTruthy() 47 | expect(warning.fix).toBe('Switch argument order in v-for="(k, item) in items" to (item, k)') 48 | }) 49 | 50 | it('matches a v-for with ind as first argument', () => { 51 | const warning = check(` 52 |

    {{ item }}, {{ ind }}

    53 | `) 54 | expect(warning).toBeTruthy() 55 | expect(warning.fix).toBe('Switch argument order in v-for="(ind, item) in items" to (item, ind)') 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /rules/vue/v-for-implicit-index-and-key.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\$(index|key)\b/, 7 | warning: function (match) { 8 | const exampleVFor = match === '$index' 9 | ? 'v-for="(item, index) in items"' 10 | : 'v-for="(value, key) in object"' 11 | return { 12 | reason: match + ' has been removed to avoid implicitly defined (i.e. "magic") variables', 13 | fix: ( 14 | 'Rename ' + chalk.red(match) + ' to ' + 15 | chalk.green(match.slice(1)) + ' and explicity declare it (e.g. ' + exampleVFor + ')' 16 | ), 17 | docsHash: 'index-and-key', 18 | type: 'template' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vue/v-for-implicit-index-and-key.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-for-implicit-index-and-key') 4 | 5 | describe('Rule: v-for-implicit-index-and-key', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches an interpolated $index', () => { 12 | const warning = check(` 13 | {{ $index }} 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Rename $index to index and explicity declare it (e.g. v-for="(item, index) in items")') 17 | }) 18 | 19 | it('matches a bound $index', () => { 20 | const warning = check(` 21 |

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Rename $index to index and explicity declare it (e.g. v-for="(item, index) in items")') 25 | }) 26 | 27 | it('matches an interpolated $key', () => { 28 | const warning = check(` 29 | {{ $key }} 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Rename $key to key and explicity declare it (e.g. v-for="(value, key) in object")') 33 | }) 34 | 35 | it('matches a bound $key', () => { 36 | const warning = check(` 37 |

    38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Rename $key to key and explicity declare it (e.g. v-for="(value, key) in object")') 41 | }) 42 | 43 | it('does not match other words', () => { 44 | const warning = check(` 45 | {{ $keyword }} 46 | `) 47 | expect(warning).toBe(null) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /rules/vue/v-for-v-model-primitive-value.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /<[^>]+\b(?:(v-for=["'][^>]+?["'])[^>]+?\b(v-model=["'][^\[\.]+["'])|(v-model=["'][^\[\.]+["'])[^>]+?(v-for=["'][^>]+?["']))[^>]*?>/, 7 | warning: function (match, vForBefore, vModelAfter, vModelBefore, vForAfter) { 8 | const vFor = vForBefore || vForAfter 9 | const vModel = vModelBefore || vModelAfter 10 | return { 11 | reason: 'v-for iterated primitives are no longer supported in v-model', 12 | fix: 'Replace ' + chalk.red(vFor) + ' with an array of objects, then update ' + chalk.red(vModel) + ' to bind to a property on each object', 13 | docsHash: 'v-model-with-v-for-Iterated-Primitive-Values', 14 | type: 'template' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/v-for-v-model-primitive-value.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-for-v-model-primitive-value') 4 | 5 | describe('Rule: v-for-v-model-primitive-value', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple v-for primitive and v-model combination, with v-model second', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace v-for="item in items" with an array of objects, then update v-model="item" to bind to a property on each object') 17 | }) 18 | 19 | it('matches a v-for primitive and v-model combination, with v-model second, surrounded by other noise', () => { 20 | const warning = check(` 21 |

    22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace v-for="item in items" with an array of objects, then update v-model="item" to bind to a property on each object') 25 | }) 26 | 27 | it('matches a simple v-for primitive and v-model combination, with v-model first', () => { 28 | const warning = check(` 29 |

    30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace v-for="item in items" with an array of objects, then update v-model="item" to bind to a property on each object') 33 | }) 34 | 35 | it('matches a v-for primitive and v-model combination, with v-model second, surrounded by other noise', () => { 36 | const warning = check(` 37 |

    38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Replace v-for="item in items" with an array of objects, then update v-model="item" to bind to a property on each object') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /rules/vue/v-leave-class.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(\.([\w-]+)-leave)[^\w-]/, 7 | warning: function (match, cssClass, name) { 8 | return { 9 | reason: 'v-leave class now defines a starting state for leave transitions, rather than the ending state', 10 | fix: ( 11 | 'Replace ' + chalk.red(cssClass) + ' with ' + 12 | chalk.green('.' + name + '-leave-active') + 13 | ' (if it\'s left over from Vue 1.x)' 14 | ), 15 | docsHash: 'Transitions', 16 | type: 'style' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rules/vue/v-leave-class.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-leave-class') 4 | 5 | describe('Rule: v-leave-class', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a v-leave-active class', () => { 12 | const warning = check(` 13 | .v-leave-active { 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches the default v-leave class', () => { 19 | const warning = check(` 20 | .v-leave { 21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Replace .v-leave with .v-leave-active (if it\'s left over from Vue 1.x)') 24 | }) 25 | 26 | it('matches a named leave class', () => { 27 | const warning = check(` 28 | .super-fade-leave { 29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Replace .super-fade-leave with .super-fade-leave-active (if it\'s left over from Vue 1.x)') 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /rules/vue/v-on-keycodes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bVue\s*\.\s*directive\s*\(\s*['"`]on['"`]\s*\)\s*\.\s*keyCodes\b/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Vue.config.keyCodes is the new, more direct way to define custom keyCodes', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green('Vue.config.keyCodes') 13 | ), 14 | docsHash: 'Vue-directive-39-on-39-keyCodes', 15 | type: 'js' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue/v-on-keycodes.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-on-keycodes') 4 | 5 | describe('Rule: v-on-keycodes', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a simple keyCode setting with the old syntax', () => { 12 | const warning = check(` 13 | Vue.directive('on').keyCodes.f1 = 112 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace Vue.directive(\'on\').keyCodes with Vue.config.keyCodes') 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /rules/vue/v-show-with-v-else.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /<[^>]+\b(?:v-else[^>]+?\b(v-show=["'][^\[\.]+["'])|(v-show=["'][^\[\.]+["'])[^>]+?v-else)[^>]*?>/, 7 | warning: function (match, vShowAfter, vShowBefore) { 8 | return { 9 | reason: 'When used with v-show, a negation expression must be used instead of v-else, e.g. v-if="!foo"', 10 | fix: ( 11 | 'Replace ' + chalk.red('v-else') + ' with a ' + 12 | chalk.green('v-if') + ' negation expression' 13 | ), 14 | docsHash: 'v-else-with-v-show', 15 | type: 'template' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rules/vue/v-show-with-v-else.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-show-with-v-else') 4 | 5 | describe('Rule: v-show-with-v-else', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match a simple v-else', () => { 12 | const warning = check(` 13 |

    14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches a simple v-show with v-else, with v-else first', () => { 19 | const warning = check(` 20 |

    21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Replace v-else with a v-if negation expression') 24 | }) 25 | 26 | it('matches a simple v-show with v-else, with v-show first', () => { 27 | const warning = check(` 28 |

    29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Replace v-else with a v-if negation expression') 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /rules/vue/v-transition-class.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(\.([\w-]+)-transition)[^\w-]/, 7 | warning: function (match, cssClass, name) { 8 | return { 9 | reason: 'v-transition class has been replaced by the standard classes used by Angular and React CSSTransitionGroup', 10 | fix: ( 11 | 'Replace ' + chalk.red(cssClass) + ' with ' + 12 | chalk.green( 13 | '.' + name + '-enter-active, ' + 14 | '.' + name + '-leave-active' 15 | ) 16 | ), 17 | docsHash: 'transition-Attribute', 18 | type: 'style' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vue/v-transition-class.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/v-transition-class') 4 | 5 | describe('Rule: v-transition-class', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match v-transition-foo class', () => { 12 | const warning = check(` 13 | .v-transition-foo { 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches the default v-transition class', () => { 19 | const warning = check(` 20 | .v-transition { 21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Replace .v-transition with .v-enter-active, .v-leave-active') 24 | }) 25 | 26 | it('matches a named transition class', () => { 27 | const warning = check(` 28 | .super-fade-transition { 29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Replace .super-fade-transition with .super-fade-enter-active, .super-fade-leave-active') 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /rules/vue/vm-delete.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(this|vm|self)\.\$delete\s*?\(\s*([^,\)]+?)\s*\)/, 7 | warning: function (match, vm, key) { 8 | const property = /['"`]\w+['"`]/.test(key) 9 | ? '.' + key.replace(/['"`]/g, '') 10 | : '[' + key + ']' 11 | return { 12 | reason: 'vm.$delete is now just an alias for Vue.delete and it is no longer possible to delete top-level reactive properties', 13 | fix: ( 14 | 'Replace ' + chalk.red(match) + ' with ' + 15 | chalk.green(vm + property + ' = null') 16 | ), 17 | docsHash: 'vm-delete', 18 | type: 'js' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vue/vm-delete.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vm-delete') 4 | 5 | describe('Rule: vm-delete', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match this.$delete(myObj, key)', () => { 12 | const warning = check(` 13 | this.$delete(myObj, key) 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches this.$delete(\'key\')', () => { 19 | const warning = check(` 20 | this.$delete('key') 21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Replace this.$delete(\'key\') with this.key = null') 24 | }) 25 | 26 | it('matches this.$delete(\'key-with-dashes\')', () => { 27 | const warning = check(` 28 | this.$delete('key-with-dashes') 29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Replace this.$delete(\'key-with-dashes\') with this[\'key-with-dashes\'] = null') 32 | }) 33 | 34 | it('matches this.$delete(`key-${with}-interpolated-string`)', () => { 35 | const warning = check(` 36 | this.$delete(\`key-\$\{with\}-interpolated-string\`) 37 | `) 38 | expect(warning).toBeTruthy() 39 | expect(warning.fix).toBe('Replace this.$delete(`key-${with}-interpolated-string`) with this[`key-${with}-interpolated-string`] = null') 40 | }) 41 | 42 | it('matches vm.$delete(\'key\')', () => { 43 | const warning = check(` 44 | vm.$delete('key') 45 | `) 46 | expect(warning).toBeTruthy() 47 | expect(warning.fix).toBe('Replace vm.$delete(\'key\') with vm.key = null') 48 | }) 49 | 50 | it('matches self.$delete(\'key\')', () => { 51 | const warning = check(` 52 | self.$delete('key') 53 | `) 54 | expect(warning).toBeTruthy() 55 | expect(warning.fix).toBe('Replace self.$delete(\'key\') with self.key = null') 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /rules/vue/vm-dom-methods.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(this|vm|self)\.\$(appendTo|before|after|remove)\s*?\(\s*?(.+?)\s*?\)/, 7 | warning: function (match, vm, method, elementOrSelector) { 8 | const element = /['"`]/.test(elementOrSelector) 9 | ? 'document.querySelector(' + elementOrSelector + ')' 10 | : elementOrSelector 11 | const replacement = { 12 | appendTo: element + '.appendChild(' + vm + '.$el' + ')', 13 | before: element + '.parentNode.insertBefore(' + vm + '.$el, ' + element + ')', 14 | after: element + '.parentNode.insertBefore(' + vm + '.$el, ' + element + '.nextSibling)', 15 | remove: vm + '.$el.remove()' 16 | }[method] 17 | return { 18 | reason: 'vm.$' + method + ' has been removed in favor of simply using the native DOM API on vm.$el', 19 | fix: ( 20 | 'Replace ' + chalk.red(match) + ' with ' + 21 | chalk.green(replacement) 22 | ), 23 | docsHash: 'vm-' + method + '', 24 | type: 'js' 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /rules/vue/vm-dom-methods.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vm-dom-methods') 4 | 5 | describe('Rule: vm-dom-methods', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches this.$appendTo(myEl)', () => { 12 | const warning = check(` 13 | this.$appendTo(myEl) 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace this.$appendTo(myEl) with myEl.appendChild(this.$el)') 17 | }) 18 | 19 | it('matches this.$appendTo("#my-el")', () => { 20 | const warning = check(` 21 | this.$appendTo("#my-el") 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace this.$appendTo("#my-el") with document.querySelector("#my-el").appendChild(this.$el)') 25 | }) 26 | 27 | it('matches vm.$appendTo("#my-el")', () => { 28 | const warning = check(` 29 | vm.$appendTo("#my-el") 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace vm.$appendTo("#my-el") with document.querySelector("#my-el").appendChild(vm.$el)') 33 | }) 34 | 35 | it('matches self.$appendTo("#my-el")', () => { 36 | const warning = check(` 37 | self.$appendTo("#my-el") 38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Replace self.$appendTo("#my-el") with document.querySelector("#my-el").appendChild(self.$el)') 41 | }) 42 | 43 | it('matches this.$before(myEl)', () => { 44 | const warning = check(` 45 | this.$before(myEl) 46 | `) 47 | expect(warning).toBeTruthy() 48 | expect(warning.fix).toBe('Replace this.$before(myEl) with myEl.parentNode.insertBefore(this.$el, myEl)') 49 | }) 50 | 51 | it('matches this.$after(myEl)', () => { 52 | const warning = check(` 53 | this.$after(myEl) 54 | `) 55 | expect(warning).toBeTruthy() 56 | expect(warning.fix).toBe('Replace this.$after(myEl) with myEl.parentNode.insertBefore(this.$el, myEl.nextSibling)') 57 | }) 58 | 59 | it('matches this.$remove(myEl)', () => { 60 | const warning = check(` 61 | this.$remove(myEl) 62 | `) 63 | expect(warning).toBeTruthy() 64 | expect(warning.fix).toBe('Replace this.$remove(myEl) with this.$el.remove()') 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /rules/vue/vm-eval.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(this|vm|self)\.\$eval\s*?\(\s*?["'`](.+?)["'`]\s*?\)/, 7 | warning: function (match, vm, property) { 8 | return { 9 | reason: 'vm.$eval has been removed, as it has no real use', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with a solution using normal JavaScript' 12 | ), 13 | docsHash: 'vm-eval', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/vm-eval.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vm-eval') 4 | 5 | describe('Rule: vm-eval', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches this.$eval(\'msg | uppercase\')', () => { 12 | const warning = check(` 13 | this.$eval('msg | uppercase') 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace this.$eval(\'msg | uppercase\') with a solution using normal JavaScript') 17 | }) 18 | 19 | it('matches vm.$eval(\'msg | uppercase\')', () => { 20 | const warning = check(` 21 | vm.$eval('msg | uppercase') 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace vm.$eval(\'msg | uppercase\') with a solution using normal JavaScript') 25 | }) 26 | 27 | it('matches self.$eval(\'msg | uppercase\')', () => { 28 | const warning = check(` 29 | self.$eval('msg | uppercase') 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace self.$eval(\'msg | uppercase\') with a solution using normal JavaScript') 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /rules/vue/vm-get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(this|vm|self)\.\$get\s*?\(\s*?["'`](.+?)["'`]\s*?\)/, 7 | warning: function (match, vm, property) { 8 | return { 9 | reason: 'vm.$get has been removed, in favor of simply accessing properties the normal JavaScript way', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green( 13 | vm + '.' + property 14 | ) 15 | ), 16 | docsHash: 'vm-get', 17 | type: 'js' 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rules/vue/vm-get.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vm-get') 4 | 5 | describe('Rule: vm-get', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches this.$get(\'foo\')', () => { 12 | const warning = check(` 13 | this.$get('foo') 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace this.$get(\'foo\') with this.foo') 17 | }) 18 | 19 | it('matches this.$get(\'foo.bar.baz\')', () => { 20 | const warning = check(` 21 | this.$get('foo.bar.baz') 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace this.$get(\'foo.bar.baz\') with this.foo.bar.baz') 25 | }) 26 | 27 | it('matches vm.$get(\'foo.bar.baz\')', () => { 28 | const warning = check(` 29 | vm.$get('foo.bar.baz') 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace vm.$get(\'foo.bar.baz\') with vm.foo.bar.baz') 33 | }) 34 | 35 | it('matches self.$get(\'foo.bar.baz\')', () => { 36 | const warning = check(` 37 | self.$get('foo.bar.baz') 38 | `) 39 | expect(warning).toBeTruthy() 40 | expect(warning.fix).toBe('Replace self.$get(\'foo.bar.baz\') with self.foo.bar.baz') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /rules/vue/vm-interpolate.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(this|vm|self)\.\$interpolate\s*?\(\s*?["'`](.+?)["'`]\s*?\)/, 7 | warning: function (match, vm, property) { 8 | return { 9 | reason: 'vm.$interpolate has been removed, as it has no real use', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with a solution using normal JavaScript' 12 | ), 13 | docsHash: 'vm-interpolate', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/vm-interpolate.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vm-interpolate') 4 | 5 | describe('Rule: vm-interpolate', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches this.$interpolate(\'{{msg}} world!\')', () => { 12 | const warning = check(` 13 | this.$interpolate('{{msg}} world!') 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace this.$interpolate(\'{{msg}} world!\') with a solution using normal JavaScript') 17 | }) 18 | 19 | it('matches vm.$interpolate(\'{{msg}} world!\')', () => { 20 | const warning = check(` 21 | vm.$interpolate('{{msg}} world!') 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace vm.$interpolate(\'{{msg}} world!\') with a solution using normal JavaScript') 25 | }) 26 | 27 | it('matches self.$interpolate(\'{{msg}} world!\')', () => { 28 | const warning = check(` 29 | self.$interpolate('{{msg}} world!') 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Replace self.$interpolate(\'{{msg}} world!\') with a solution using normal JavaScript') 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /rules/vue/vm-log.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(this|vm|self)\.\$log\s*?\(\s*?["'`](.+?)["'`]\s*?\)/, 7 | warning: function (match, vm, property) { 8 | return { 9 | reason: 'vm.$log has been removed, because the Vue Devtools offer a better debugging experience', 10 | fix: ( 11 | 'Delete ' + chalk.red(match) + ' and install the Vue Devtools instead: ' + chalk.underline.green('https://github.com/vuejs/vue-devtools') 12 | ), 13 | docsHash: 'vm-log', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/vm-log.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vm-log') 4 | 5 | describe('Rule: vm-log', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches this.$log(\'{{msg}} world!\')', () => { 12 | const warning = check(` 13 | this.$log('{{msg}} world!') 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Delete this.$log(\'{{msg}} world!\') and install the Vue Devtools instead: https://github.com/vuejs/vue-devtools') 17 | }) 18 | 19 | it('matches vm.$log(\'{{msg}} world!\')', () => { 20 | const warning = check(` 21 | vm.$log('{{msg}} world!') 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Delete vm.$log(\'{{msg}} world!\') and install the Vue Devtools instead: https://github.com/vuejs/vue-devtools') 25 | }) 26 | 27 | it('matches self.$log(\'{{msg}} world!\')', () => { 28 | const warning = check(` 29 | self.$log('{{msg}} world!') 30 | `) 31 | expect(warning).toBeTruthy() 32 | expect(warning.fix).toBe('Delete self.$log(\'{{msg}} world!\') and install the Vue Devtools instead: https://github.com/vuejs/vue-devtools') 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /rules/vue/vm-set.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(this|vm|self)\.\$set\s*?\(\s*['"`]([\w\.]+?)\.(\w+)['"`]\s*,\s*([^,]+?)\s*\)/, 7 | warning: function (match, vm, objectPath, leafProperty, value) { 8 | return { 9 | reason: 'vm.$set is now just an alias for Vue.set', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green( 13 | vm + '.$set(' + 14 | vm + '.' + objectPath + ', ' + 15 | '\'' + leafProperty + '\'' + ', ' + 16 | value + 17 | ')' 18 | ) 19 | ), 20 | docsHash: 'vm-set', 21 | type: 'js' 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /rules/vue/vm-set.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vm-set') 4 | 5 | describe('Rule: vm-set', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match this.$set(obj, \'foo\', 42)', () => { 12 | const warning = check(` 13 | this.$set(obj, 'foo', 42) 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches this.$set(\'foo.bar\', 42)', () => { 19 | const warning = check(` 20 | this.$set('foo.bar', 42) 21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Replace this.$set(\'foo.bar\', 42) with this.$set(this.foo, \'bar\', 42)') 24 | }) 25 | 26 | it('matches this.$set(\'foo.bar.baz\', 42)', () => { 27 | const warning = check(` 28 | this.$set('foo.bar.baz', 42) 29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Replace this.$set(\'foo.bar.baz\', 42) with this.$set(this.foo.bar, \'baz\', 42)') 32 | }) 33 | 34 | it('matches vm.$set(\'foo.bar.baz\', 42)', () => { 35 | const warning = check(` 36 | vm.$set('foo.bar.baz', 42) 37 | `) 38 | expect(warning).toBeTruthy() 39 | expect(warning.fix).toBe('Replace vm.$set(\'foo.bar.baz\', 42) with vm.$set(vm.foo.bar, \'baz\', 42)') 40 | }) 41 | 42 | it('matches self.$set(\'foo.bar.baz\', 42)', () => { 43 | const warning = check(` 44 | self.$set('foo.bar.baz', 42) 45 | `) 46 | expect(warning).toBeTruthy() 47 | expect(warning.fix).toBe('Replace self.$set(\'foo.bar.baz\', 42) with self.$set(self.foo.bar, \'baz\', 42)') 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /rules/vue/vue-config-async.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /Vue\.config\.async/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Async is now always required for performance', 10 | fix: 'Delete references to ' + chalk.red('Vue.config.async'), 11 | docsHash: 'Vue-config-async', 12 | type: 'js' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rules/vue/vue-config-async.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-config-async') 4 | 5 | describe('Rule: vue-config-async', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches async set to true', () => { 12 | const warning = check(` 13 | Vue.config.async = true 14 | `) 15 | expect(warning).toBeTruthy() 16 | }) 17 | 18 | it('matches async set to false', () => { 19 | const warning = check(` 20 | Vue.config.async = false 21 | `) 22 | expect(warning).toBeTruthy() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /rules/vue/vue-config-debug.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /Vue\.config\.debug/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Warnings come with stack traces by default now, making this option redundant', 10 | fix: 'Delete references to ' + chalk.red('Vue.config.debug'), 11 | docsHash: 'Vue-config-debug', 12 | type: 'js' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rules/vue/vue-config-debug.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-config-debug') 4 | 5 | describe('Rule: vue-config-debug', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches debug set to true', () => { 12 | const warning = check(` 13 | Vue.config.debug = true 14 | `) 15 | expect(warning).toBeTruthy() 16 | }) 17 | 18 | it('matches debug set to false', () => { 19 | const warning = check(` 20 | Vue.config.debug = false 21 | `) 22 | expect(warning).toBeTruthy() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /rules/vue/vue-config-delimiters.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /Vue\.config\.delimiters/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Delimiters are now a component-level option', 10 | fix: 'Refactor ' + chalk.red('Vue.config.delimiters') + ' to its component-level equivalent', 11 | docsHash: 'Vue-config-delimiters', 12 | type: 'js' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rules/vue/vue-config-delimiters.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-config-delimiters') 4 | 5 | describe('Rule: vue-config-delimiters', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches delimiters set to true', () => { 12 | const warning = check(` 13 | Vue.config.delimiters = true 14 | `) 15 | expect(warning).toBeTruthy() 16 | }) 17 | 18 | it('matches delimiters set to false', () => { 19 | const warning = check(` 20 | Vue.config.delimiters = false 21 | `) 22 | expect(warning).toBeTruthy() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /rules/vue/vue-config-unsafe-delimiters.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /Vue\.config\.unsafeDelimiters/, 7 | warning: function (match) { 8 | return { 9 | reason: 'Unsafe interpolation has been removed', 10 | fix: 'Delete ' + chalk.red('Vue.config.unsafeDelimiters') + ' and replace all instances of unsafe interpolations with ' + chalk.green('v-html'), 11 | docsHash: 'Vue-config-unsafeDelimiters', 12 | type: 'js' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rules/vue/vue-config-unsafe-delimiters.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-config-unsafe-delimiters') 4 | 5 | describe('Rule: vue-config-unsafe-delimiters', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches unsafeDelimiters set to true', () => { 12 | const warning = check(` 13 | Vue.config.unsafeDelimiters = true 14 | `) 15 | expect(warning).toBeTruthy() 16 | }) 17 | 18 | it('matches unsafeDelimiters set to false', () => { 19 | const warning = check(` 20 | Vue.config.unsafeDelimiters = false 21 | `) 22 | expect(warning).toBeTruthy() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /rules/vue/vue-delete-vm.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /Vue\.delete\(\s*?(this|vm|self)\s*?,([^,]+?)\)/, 7 | warning: function (match, vm, property) { 8 | return { 9 | reason: 'Vue.set and Vue.delete no longer work on Vue instances - it is now mandatory to properly declare all top-level reactive properties in the data option', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green( 13 | 'Vue.delete(' + 14 | vm + '.newTopLevelObject, ' + 15 | property.trim() + 16 | ')' 17 | ) + 18 | ', then scope ' + property.replace(/['"]/g, '').trim() + ' underneath newTopLevelObject, rather than declaring it as a top-level $data property' 19 | ), 20 | docsHash: 'Vue-set-and-Vue-delete-on-Vue-instances', 21 | type: 'js' 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /rules/vue/vue-delete-vm.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-delete-vm') 4 | 5 | describe('Rule: vue-delete-vm', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match Vue.delete(foo, \'bar\')', () => { 12 | const warning = check(` 13 | Vue.delete(foo, 'bar') 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches Vue.delete(this, \'foo\')', () => { 19 | const warning = check(` 20 | Vue.delete(this, 'foo') 21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Replace Vue.delete(this, \'foo\') with Vue.delete(this.newTopLevelObject, \'foo\'), then scope foo underneath newTopLevelObject, rather than declaring it as a top-level $data property') 24 | }) 25 | 26 | it('matches Vue.delete(vm, \'foo\')', () => { 27 | const warning = check(` 28 | Vue.delete(vm, 'foo') 29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Replace Vue.delete(vm, \'foo\') with Vue.delete(vm.newTopLevelObject, \'foo\'), then scope foo underneath newTopLevelObject, rather than declaring it as a top-level $data property') 32 | }) 33 | 34 | it('matches Vue.delete(self, \'foo\')', () => { 35 | const warning = check(` 36 | Vue.delete(self, 'foo') 37 | `) 38 | expect(warning).toBeTruthy() 39 | expect(warning.fix).toBe('Replace Vue.delete(self, \'foo\') with Vue.delete(self.newTopLevelObject, \'foo\'), then scope foo underneath newTopLevelObject, rather than declaring it as a top-level $data property') 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /rules/vue/vue-dependency.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /("vue"\s*:\s*)"[^\d"]*?[^2-9]\.\d+\.\d+"/, 7 | warning: function (match, preVersion) { 8 | return { 9 | reason: 'If you are using pre-2.0 Vue through NPM, you have to update it in your package.json file', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green( 13 | preVersion + '"^2.0.0"' 14 | ) + 15 | ', then run: npm install' 16 | ), 17 | docsHash: '', 18 | type: 'package.json' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vue/vue-dependency.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-dependency') 4 | 5 | describe('Rule: vue-dependency', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "^2.0.0"', () => { 12 | const warning = check(` 13 | "vue": "^2.0.0" 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match "^2.0.0" with comma', () => { 19 | const warning = check(` 20 | "vue": "^2.0.0", 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match "^2.0.1"', () => { 26 | const warning = check(` 27 | "vue": "^2.0.1" 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('does not match "2.0.0"', () => { 33 | const warning = check(` 34 | "vue": "2.0.0" 35 | `) 36 | expect(warning).toBe(null) 37 | }) 38 | 39 | it('is not limited to pre 3.0 versions', () => { 40 | const warning = check(` 41 | "vue": "3.0.0" 42 | `) 43 | expect(warning).toBe(null) 44 | }) 45 | 46 | it('is not limited to pre 3.0 versions, with comma', () => { 47 | const warning = check(` 48 | "vue": "3.0.0", 49 | `) 50 | expect(warning).toBe(null) 51 | }) 52 | 53 | it('does not match vuex "1.0.0"', () => { 54 | const warning = check(` 55 | "vuex": "1.0.0" 56 | `) 57 | expect(warning).toBe(null) 58 | }) 59 | 60 | it('matches "^1.0.27"', () => { 61 | const warning = check(` 62 | "vue": "^1.0.27" 63 | `) 64 | expect(warning).toBeTruthy() 65 | expect(warning.fix).toBe('Replace "vue": "^1.0.27" with "vue": "^2.0.0", then run: npm install') 66 | }) 67 | 68 | it('matches "^1.0.27" with comma', () => { 69 | const warning = check(` 70 | "vue": "^1.0.27", 71 | `) 72 | expect(warning).toBeTruthy() 73 | expect(warning.fix).toBe('Replace "vue": "^1.0.27" with "vue": "^2.0.0", then run: npm install') 74 | }) 75 | 76 | it('matches "1.0.27"', () => { 77 | const warning = check(` 78 | "vue": "1.0.27", 79 | `) 80 | expect(warning).toBeTruthy() 81 | expect(warning.fix).toBe('Replace "vue": "1.0.27" with "vue": "^2.0.0", then run: npm install') 82 | }) 83 | 84 | it('matches "0.12.1"', () => { 85 | const warning = check(` 86 | "vue": "0.12.1", 87 | `) 88 | expect(warning).toBeTruthy() 89 | expect(warning.fix).toBe('Replace "vue": "0.12.1" with "vue": "^2.0.0", then run: npm install') 90 | }) 91 | }) 92 | -------------------------------------------------------------------------------- /rules/vue/vue-directive.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /Vue\.directive\s*\(\s*(['"`].+['"`])/, 7 | warning: function (match, name) { 8 | return { 9 | reason: 'Directives have been vastly reduced in scope and components are preferred in most use cases', 10 | fix: ( 11 | 'Check ' + chalk.red('Vue.directive(' + name + ')') + ' to make sure its syntax has been updated and for anything beyond simple DOM manipulations, refactor to a component' 12 | ), 13 | docsHash: 'Custom-Directives-simplified', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/vue-directive.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-directive') 4 | 5 | describe('Rule: vue-directive', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a directive declaration', () => { 12 | const warning = check(` 13 | Vue.directive('foo', { 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Check Vue.directive(\'foo\') to make sure its syntax has been updated and for anything beyond simple DOM manipulations, refactor to a component') 17 | }) 18 | 19 | it('matches a directive declaration with double quotes', () => { 20 | const warning = check(` 21 | Vue.directive("foo", { 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Check Vue.directive("foo") to make sure its syntax has been updated and for anything beyond simple DOM manipulations, refactor to a component') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue/vue-element-directive.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /Vue\.elementDirective\s*\(\s*(['"`].+['"`])/, 7 | warning: function (match, name) { 8 | return { 9 | reason: 'Element directives have been removed in favor of components', 10 | fix: ( 11 | 'Replace ' + chalk.red('Vue.elementDirective(' + name + ')') + ' with a component' 12 | ), 13 | docsHash: 'Vue-elementDirective', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/vue-element-directive.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-element-directive') 4 | 5 | describe('Rule: vue-element-directive', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches an element directive declaration', () => { 12 | const warning = check(` 13 | Vue.elementDirective('foo', { 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace Vue.elementDirective(\'foo\') with a component') 17 | }) 18 | 19 | it('matches an element directive declaration with double quotes', () => { 20 | const warning = check(` 21 | Vue.elementDirective("foo", { 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace Vue.elementDirective("foo") with a component') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue/vue-loader-dependency.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /("vue-loader"\s*:\s*)"[^\d"]*?[^9]\.\d+\.\d+"/, 7 | warning: function (match, preVersion) { 8 | return { 9 | reason: 'vue-loader 9.0 is the earliest supported version compatible with Vue 2.0', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green( 13 | preVersion + '"^9.0.0"' 14 | ) + 15 | ', then run: npm install' 16 | ), 17 | docsHash: '', 18 | type: 'package.json' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vue/vue-loader-dependency.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-loader-dependency') 4 | 5 | describe('Rule: vue-loader-dependency', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "^9.0.0"', () => { 12 | const warning = check(` 13 | "vue-loader": "^9.0.0" 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match "^9.0.0" with comma', () => { 19 | const warning = check(` 20 | "vue-loader": "^9.0.0", 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match "^9.0.1"', () => { 26 | const warning = check(` 27 | "vue-loader": "^9.0.1" 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('does not match "9.0.0"', () => { 33 | const warning = check(` 34 | "vue-loader": "9.0.0" 35 | `) 36 | expect(warning).toBe(null) 37 | }) 38 | 39 | it('does not match vuex "9.0.0"', () => { 40 | const warning = check(` 41 | "vuex": "9.0.0" 42 | `) 43 | expect(warning).toBe(null) 44 | }) 45 | 46 | it('matches "^7.1.5"', () => { 47 | const warning = check(` 48 | "vue-loader": "^7.1.5" 49 | `) 50 | expect(warning).toBeTruthy() 51 | expect(warning.fix).toBe('Replace "vue-loader": "^7.1.5" with "vue-loader": "^9.0.0", then run: npm install') 52 | }) 53 | 54 | it('matches "^7.1.5" with comma', () => { 55 | const warning = check(` 56 | "vue-loader": "^7.1.5", 57 | `) 58 | expect(warning).toBeTruthy() 59 | expect(warning.fix).toBe('Replace "vue-loader": "^7.1.5" with "vue-loader": "^9.0.0", then run: npm install') 60 | }) 61 | 62 | it('matches "7.1.5"', () => { 63 | const warning = check(` 64 | "vue-loader": "7.1.5", 65 | `) 66 | expect(warning).toBeTruthy() 67 | expect(warning.fix).toBe('Replace "vue-loader": "7.1.5" with "vue-loader": "^9.0.0", then run: npm install') 68 | }) 69 | 70 | it('matches "0.12.1"', () => { 71 | const warning = check(` 72 | "vue-loader": "0.12.1", 73 | `) 74 | expect(warning).toBeTruthy() 75 | expect(warning.fix).toBe('Replace "vue-loader": "0.12.1" with "vue-loader": "^9.0.0", then run: npm install') 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /rules/vue/vue-partial.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /Vue\.partial\s*\(\s*(['"`].+['"`])/, 7 | warning: function (match, name) { 8 | return { 9 | reason: 'Partials have been removed in favor of functional components', 10 | fix: ( 11 | 'Replace ' + chalk.red('Vue.partial(' + name + ')') + ' with a normal component that receives props - or a functional component in performance-critical situations' 12 | ), 13 | docsHash: 'Vue-partial', 14 | type: 'js' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rules/vue/vue-partial.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-partial') 4 | 5 | describe('Rule: vue-partial', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a partial declaration', () => { 12 | const warning = check(` 13 | Vue.partial('foo', { 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace Vue.partial(\'foo\') with a normal component that receives props - or a functional component in performance-critical situations') 17 | }) 18 | 19 | it('matches a partial declaration with double quotes', () => { 20 | const warning = check(` 21 | Vue.partial("foo", { 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace Vue.partial("foo") with a normal component that receives props - or a functional component in performance-critical situations') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vue/vue-set-vm.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /(Vue\.set|(?:this|vm|self)\.\$set)\(\s*?(this|vm|self)\s*?,([^,]+?),([^,]+?)\)/, 7 | warning: function (match, command, vm, property, value) { 8 | const formattedProperty = property.replace(/['"]/g, '').trim() 9 | return { 10 | reason: 'Vue.set and Vue.delete no longer work on Vue instances - it is now mandatory to properly declare all top-level reactive properties in the data option', 11 | fix: ( 12 | 'Replace ' + chalk.red(match) + ' with ' + 13 | chalk.green( 14 | vm + '.' + formattedProperty + ' = ' + value.trim() 15 | ) + 16 | ' and declare ' + formattedProperty + ' in the data option with an initial value' 17 | ), 18 | docsHash: 'Vue-set-and-Vue-delete-on-Vue-instances', 19 | type: 'js' 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /rules/vue/vue-set-vm.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-set-vm') 4 | 5 | describe('Rule: vue-set-vm', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match Vue.set(foo, \'bar\', 42)', () => { 12 | const warning = check(` 13 | Vue.set(foo, 'bar', 42) 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('matches Vue.set(this, \'foo\', 42)', () => { 19 | const warning = check(` 20 | Vue.set(this, 'foo', 42) 21 | `) 22 | expect(warning).toBeTruthy() 23 | expect(warning.fix).toBe('Replace Vue.set(this, \'foo\', 42) with this.foo = 42 and declare foo in the data option with an initial value') 24 | }) 25 | 26 | it('matches Vue.set(vm, \'foo\', 42)', () => { 27 | const warning = check(` 28 | Vue.set(vm, 'foo', 42) 29 | `) 30 | expect(warning).toBeTruthy() 31 | expect(warning.fix).toBe('Replace Vue.set(vm, \'foo\', 42) with vm.foo = 42 and declare foo in the data option with an initial value') 32 | }) 33 | 34 | it('matches Vue.set(self, \'foo\', 42)', () => { 35 | const warning = check(` 36 | Vue.set(self, 'foo', 42) 37 | `) 38 | expect(warning).toBeTruthy() 39 | expect(warning.fix).toBe('Replace Vue.set(self, \'foo\', 42) with self.foo = 42 and declare foo in the data option with an initial value') 40 | }) 41 | 42 | it('matches vm.$set(this, \'foo\', 42)', () => { 43 | const warning = check(` 44 | vm.$set(this, 'foo', 42) 45 | `) 46 | expect(warning).toBeTruthy() 47 | expect(warning.fix).toBe('Replace vm.$set(this, \'foo\', 42) with this.foo = 42 and declare foo in the data option with an initial value') 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /rules/vue/vue-transition.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /Vue\.transition\s*\(\s*(['"`].+['"`])/, 7 | warning: function (match, name) { 8 | return { 9 | reason: 'Vue.transition has been removed, as the new transition system uses components for reusable transitions', 10 | fix: ( 11 | 'Replace ' + chalk.red('Vue.transition(' + name + ')') + ' with a component that uses the new ' + 12 | chalk.green('') + ' or ' + 13 | chalk.green('') + 14 | ' element as its root' 15 | ), 16 | docsHash: 'Vue-transition-for-Reusable-Transitions', 17 | type: 'js' 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rules/vue/vue-transition.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vue/vue-transition') 4 | 5 | describe('Rule: vue-transition', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('matches a transition declaration', () => { 12 | const warning = check(` 13 | Vue.transition('foo', { 14 | `) 15 | expect(warning).toBeTruthy() 16 | expect(warning.fix).toBe('Replace Vue.transition(\'foo\') with a component that uses the new or element as its root') 17 | }) 18 | 19 | it('matches a transition declaration with double quotes', () => { 20 | const warning = check(` 21 | Vue.transition("foo", { 22 | `) 23 | expect(warning).toBeTruthy() 24 | expect(warning.fix).toBe('Replace Vue.transition("foo") with a component that uses the new or element as its root') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /rules/vuex/store-event-emitter.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /\bstore\s*.\s*(on|emit|off)\b/, 7 | warning: function (match, method) { 8 | var eventBus = chalk.green('eventBus.' + method) + ', creating the eventBus from an empty Vue instance' 9 | var replacement = method === 'on' 10 | ? chalk.green('store.subscribe') + ' or ' + eventBus 11 | : eventBus 12 | return { 13 | reason: 'The store no longer exposes an event emitter interface', 14 | fix: ( 15 | 'Replace ' + chalk.red(match) + ' with ' + replacement + ' (see the link below for details)' 16 | ), 17 | docsHash: 'Store’s-Event-Emitter', 18 | type: 'js' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vuex/store-event-emitter.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vuex/store-event-emitter') 4 | 5 | describe('Rule: store-event-emitter', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match store.watch', () => { 12 | const warning = check(` 13 | store.watch 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match store.once', () => { 19 | const warning = check(` 20 | store.watch 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('matches store.on', () => { 26 | const warning = check(` 27 | store.on 28 | `) 29 | expect(warning).toBeTruthy() 30 | expect(warning.fix).toBe('Replace store.on with store.subscribe or eventBus.on, creating the eventBus from an empty Vue instance (see the link below for details)') 31 | }) 32 | 33 | it('matches store.on(', () => { 34 | const warning = check(` 35 | store.on( 36 | `) 37 | expect(warning).toBeTruthy() 38 | expect(warning.fix).toBe('Replace store.on with store.subscribe or eventBus.on, creating the eventBus from an empty Vue instance (see the link below for details)') 39 | }) 40 | 41 | it('matches store.emit', () => { 42 | const warning = check(` 43 | store.emit 44 | `) 45 | expect(warning).toBeTruthy() 46 | expect(warning.fix).toBe('Replace store.emit with eventBus.emit, creating the eventBus from an empty Vue instance (see the link below for details)') 47 | }) 48 | 49 | it('matches store.off', () => { 50 | const warning = check(` 51 | store.off 52 | `) 53 | expect(warning).toBeTruthy() 54 | expect(warning.fix).toBe('Replace store.off with eventBus.off, creating the eventBus from an empty Vue instance (see the link below for details)') 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /rules/vuex/store-watch-with-string.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | pattern: /\bstore\s*.\s*watch\s*\(\s*['"`]/, 5 | warning: function (match) { 6 | return { 7 | reason: 'The new store.watch takes a function as the first argument, allowing you to watch not only state, but also custom getters', 8 | fix: ( 9 | 'Update store.watch to use a function rather than a string path for the first argument (see link below for an example)' 10 | ), 11 | docsHash: 'store-watch-with-String-Property-Path', 12 | type: 'js' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rules/vuex/store-watch-with-string.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vuex/store-watch-with-string') 4 | 5 | describe('Rule: store-watch-with-string', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match store.watch with no arguments', () => { 12 | const warning = check(` 13 | store.watch( 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match store.watch with arrow function arg', () => { 19 | const warning = check(` 20 | store.watch(state => { 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match store.watch with regular function arg', () => { 26 | const warning = check(` 27 | store.watch(function (state) { 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('matches store.watch with a string argument', () => { 33 | const warning = check(` 34 | store.watch('user.notifications', callback) 35 | `) 36 | expect(warning).toBeTruthy() 37 | expect(warning.fix).toBe('Update store.watch to use a function rather than a string path for the first argument (see link below for an example)') 38 | }) 39 | 40 | it('matches store.watch with a string argument and a bunch of spaces', () => { 41 | const warning = check(` 42 | store . watch ( 'user.notifications' , callback) 43 | `) 44 | expect(warning).toBeTruthy() 45 | expect(warning.fix).toBe('Update store.watch to use a function rather than a string path for the first argument (see link below for an example)') 46 | }) 47 | 48 | it('matches store.watch with a string argument using double quotes', () => { 49 | const warning = check(` 50 | store.watch('user.notifications', callback) 51 | `) 52 | expect(warning).toBeTruthy() 53 | expect(warning.fix).toBe('Update store.watch to use a function rather than a string path for the first argument (see link below for an example)') 54 | }) 55 | 56 | it('matches store.watch with a string argument using backticks', () => { 57 | const warning = check(` 58 | store.watch(\`user.notifications\`, callback) 59 | `) 60 | expect(warning).toBeTruthy() 61 | expect(warning.fix).toBe('Update store.watch to use a function rather than a string path for the first argument (see link below for an example)') 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /rules/vuex/vuex-dependency.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var chalk = require('chalk') 4 | 5 | module.exports = { 6 | pattern: /("vuex"\s*:\s*)"[^\d"]*?[^1-9]\.\d+\.\d+"/, 7 | warning: function (match, preVersion) { 8 | return { 9 | reason: 'Vuex 1.0 is the earliest supported version compatible with Vue 2.0', 10 | fix: ( 11 | 'Replace ' + chalk.red(match) + ' with ' + 12 | chalk.green( 13 | preVersion + '"^1.0.0"' 14 | ) + 15 | ', then run: npm install' 16 | ), 17 | docsHash: '', 18 | type: 'package.json' 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rules/vuex/vuex-dependency.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vuex/vuex-dependency') 4 | 5 | describe('Rule: vuex-dependency', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "^1.0.0"', () => { 12 | const warning = check(` 13 | "vuex": "^1.0.0" 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match "^1.0.0" with comma', () => { 19 | const warning = check(` 20 | "vuex": "^1.0.0", 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('does not match "^1.0.1"', () => { 26 | const warning = check(` 27 | "vuex": "^1.0.1" 28 | `) 29 | expect(warning).toBe(null) 30 | }) 31 | 32 | it('does not match "^2.0.1"', () => { 33 | const warning = check(` 34 | "vuex": "^2.0.1" 35 | `) 36 | expect(warning).toBe(null) 37 | }) 38 | 39 | it('does not match "1.0.0"', () => { 40 | const warning = check(` 41 | "vuex": "1.0.0" 42 | `) 43 | expect(warning).toBe(null) 44 | }) 45 | 46 | it('does not match "2.0.0"', () => { 47 | const warning = check(` 48 | "vuex": "2.0.0" 49 | `) 50 | expect(warning).toBe(null) 51 | }) 52 | 53 | it('does not match vue-loader "0.6.0"', () => { 54 | const warning = check(` 55 | "vue-loader": "0.6.0" 56 | `) 57 | expect(warning).toBe(null) 58 | }) 59 | 60 | it('matches "^0.0.27"', () => { 61 | const warning = check(` 62 | "vuex": "^0.0.27" 63 | `) 64 | expect(warning).toBeTruthy() 65 | expect(warning.fix).toBe('Replace "vuex": "^0.0.27" with "vuex": "^1.0.0", then run: npm install') 66 | }) 67 | 68 | it('matches "^0.0.27" with comma', () => { 69 | const warning = check(` 70 | "vuex": "^0.0.27", 71 | `) 72 | expect(warning).toBeTruthy() 73 | expect(warning.fix).toBe('Replace "vuex": "^0.0.27" with "vuex": "^1.0.0", then run: npm install') 74 | }) 75 | 76 | it('matches "0.0.27"', () => { 77 | const warning = check(` 78 | "vuex": "0.0.27", 79 | `) 80 | expect(warning).toBeTruthy() 81 | expect(warning.fix).toBe('Replace "vuex": "0.0.27" with "vuex": "^1.0.0", then run: npm install') 82 | }) 83 | 84 | it('matches "0.6.1"', () => { 85 | const warning = check(` 86 | "vuex": "0.6.1", 87 | `) 88 | expect(warning).toBeTruthy() 89 | expect(warning.fix).toBe('Replace "vuex": "0.6.1" with "vuex": "^1.0.0", then run: npm install') 90 | }) 91 | }) 92 | -------------------------------------------------------------------------------- /rules/vuex/vuex-middlewares.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | pattern: /\bmiddlewares:/, 5 | warning: function (match) { 6 | return { 7 | reason: 'Vuex middlewares have been replaced by a new plugin system', 8 | fix: ( 9 | 'Rewrite your Vuex middlewares as plugins (see the link below for an example)' 10 | ), 11 | docsHash: 'Middlewares', 12 | type: 'js' 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rules/vuex/vuex-middlewares.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const check = createRuleChecker('vuex/vuex-middlewares') 4 | 5 | describe('Rule: vuex-middlewares', () => { 6 | it('does not match an empty line', () => { 7 | const warning = check('') 8 | expect(warning).toBe(null) 9 | }) 10 | 11 | it('does not match "middlewares"', () => { 12 | const warning = check(` 13 | middlewares 14 | `) 15 | expect(warning).toBe(null) 16 | }) 17 | 18 | it('does not match "middlewares"', () => { 19 | const warning = check(` 20 | foomiddlewares 21 | `) 22 | expect(warning).toBe(null) 23 | }) 24 | 25 | it('matches "middlewares"', () => { 26 | const warning = check(` 27 | middlewares: 28 | `) 29 | expect(warning).toBeTruthy() 30 | expect(warning.fix).toBe('Rewrite your Vuex middlewares as plugins (see the link below for an example)') 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /spec/e2e/basic.spec.js: -------------------------------------------------------------------------------- 1 | describe('scanning empty directory', () => { 2 | it('completes without error', () => { 3 | const result = runMigrationHelper(['empty']) 4 | expect(result.error).toBe(undefined) 5 | expect(result.stderr).toBe('') 6 | expect(result.stdout).toContain('No obsolete syntax was detected') 7 | }) 8 | }) 9 | 10 | describe('scanning directory with an empty file', () => { 11 | it('completes without error', () => { 12 | const result = runMigrationHelper(['empty-file']) 13 | expect(result.error).toBe(undefined) 14 | expect(result.stderr).toBe('') 15 | expect(result.stdout).toContain('No obsolete syntax was detected') 16 | }) 17 | }) 18 | 19 | describe('scanning directory with a simple js file', () => { 20 | it('completes without error', () => { 21 | const result = runMigrationHelper(['simple']) 22 | expect(result.error).toBe(undefined) 23 | expect(result.stderr).toBe('') 24 | expect(result.stdout).toContain('1. ') 25 | }) 26 | }) 27 | 28 | describe('scanning a simple js file', () => { 29 | it('completes without error', () => { 30 | const result = runMigrationHelper(['simple/main.js']) 31 | expect(result.error).toBe(undefined) 32 | expect(result.stderr).toBe('') 33 | expect(result.stdout).toContain('1. ') 34 | }) 35 | }) 36 | 37 | describe('scanning a directory of files with matching but ignorable warnings', () => { 38 | it('completes without error', () => { 39 | const result = runMigrationHelper(['ignorable']) 40 | expect(result.error).toBe(undefined) 41 | expect(result.stderr).toBe('') 42 | expect(result.stdout).toContain('No obsolete syntax was detected') 43 | }) 44 | }) 45 | 46 | describe('scanning a directory that includes weird extensions', () => { 47 | it('completes without error', () => { 48 | const result = runMigrationHelper(['weird-extension']) 49 | expect(result.error).toBe(undefined) 50 | expect(result.stderr).toBe('') 51 | expect(result.stdout).toContain('No obsolete syntax was detected') 52 | }) 53 | }) 54 | 55 | describe('scanning a directory that includes package.json', () => { 56 | it('completes without error', () => { 57 | const result = runMigrationHelper(['package-json']) 58 | expect(result.error).toBe(undefined) 59 | expect(result.stderr).toBe('') 60 | expect(result.stdout).toContain('Line 3:') 61 | }) 62 | }) 63 | 64 | -------------------------------------------------------------------------------- /spec/fixtures/projects/empty-file/main.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs/vue-migration-helper/aa3e8c1d990d76632ec187e6fa61d053438533c0/spec/fixtures/projects/empty-file/main.js -------------------------------------------------------------------------------- /spec/fixtures/projects/ignorable/main.js: -------------------------------------------------------------------------------- 1 | const meta = { vue: '^1.0.24' } 2 | console.log(meta) 3 | -------------------------------------------------------------------------------- /spec/fixtures/projects/ignorable/style.css: -------------------------------------------------------------------------------- 1 | $index 2 | -------------------------------------------------------------------------------- /spec/fixtures/projects/package-json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "vue": "^1.0.24" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /spec/fixtures/projects/simple/main.js: -------------------------------------------------------------------------------- 1 | /* global Vue */ 2 | /* eslint-disable no-new */ 3 | 4 | new Vue({ 5 | replace: false 6 | }) 7 | -------------------------------------------------------------------------------- /spec/fixtures/projects/weird-extension/some-file.blarg: -------------------------------------------------------------------------------- 1 | arst 2 | -------------------------------------------------------------------------------- /spec/helpers/catch-log.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const stripAnsi = require('strip-ansi') 4 | 5 | global.catchLog = (logger, callback) => { 6 | const origLog = console.log 7 | console.log = jasmine.createSpy('log') 8 | 9 | function sanitizeLogs () { 10 | const stdout = console.log.calls.all().map(call => { 11 | return call.args.map(arg => { 12 | return stripAnsi(arg) 13 | }).join('') 14 | }).join('\n') 15 | console.log = origLog 16 | if (callback) { 17 | callback(stdout) 18 | } 19 | return stdout 20 | } 21 | 22 | if (callback) { 23 | logger(() => { 24 | sanitizeLogs() 25 | }) 26 | } else { 27 | logger() 28 | return sanitizeLogs() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spec/helpers/create-rule-checker.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | global.createRuleChecker = ruleName => { 4 | const stripAnsi = require('strip-ansi') 5 | const assertRule = require('../../helpers/assert-rule') 6 | 7 | const rule = require('../../rules/' + ruleName) 8 | return function (line) { 9 | const warning = assertRule({ 10 | line: line, 11 | lineNum: 42, 12 | file: 'file.js' 13 | }, rule) 14 | if (warning) { 15 | warning.fix = stripAnsi(warning.fix) 16 | } 17 | return warning 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spec/helpers/run-migration-helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const spawnSync = require('child_process').spawnSync 5 | 6 | global.runMigrationHelper = filesAndOrFolders => { 7 | const results = spawnSync( 8 | path.join(__dirname, '../../index.js'), 9 | filesAndOrFolders.map(filesAndOrFolder => 'spec/fixtures/projects/' + filesAndOrFolder), 10 | { 11 | cwd: path.join(__dirname, '../..') 12 | } 13 | ) 14 | 15 | results.stderr = results.stderr.toString() 16 | results.stdout = results.stdout.toString() 17 | 18 | return results 19 | } 20 | --------------------------------------------------------------------------------