├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── lib ├── index.js ├── options.js ├── processors │ ├── fixer.js │ └── vueProcessor.js └── rules │ └── vuefixRule.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | 30 | # ========================= 31 | # Operating System Files 32 | # ========================= 33 | 34 | # OSX 35 | # ========================= 36 | 37 | .DS_Store 38 | .AppleDouble 39 | .LSOverride 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear in the root of a volume 45 | .DocumentRevisions-V100 46 | .fseventsd 47 | .Spotlight-V100 48 | .TemporaryItems 49 | .Trashes 50 | .VolumeIcon.icns 51 | 52 | # Directories potentially created on remote AFP share 53 | .AppleDB 54 | .AppleDesktop 55 | Network Trash Folder 56 | Temporary Items 57 | .apdisk 58 | 59 | # Windows 60 | # ========================= 61 | 62 | # Windows image file caches 63 | Thumbs.db 64 | ehthumbs.db 65 | 66 | # Folder config file 67 | Desktop.ini 68 | 69 | # Recycle Bin used on file shares 70 | $RECYCLE.BIN/ 71 | 72 | # Windows Installer files 73 | *.cab 74 | *.msi 75 | *.msm 76 | *.msp 77 | 78 | # Windows shortcuts 79 | *.lnk 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 lkiarest 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-vuefix 2 | 3 | This ESLint plugin extracts and auto-fix scripts from .vue files. 4 | 5 | __For vue-cli@3, please use [official vue eslint plugin](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint)__ 6 | 7 | ## Usage 8 | 9 | Simply install via npm install --save-dev eslint-plugin-vuefix and add the plugin to your ESLint configuration. See [ESLint documentation](http://eslint.org/docs/user-guide/configuring#configuring-plugins). 10 | 11 | Plugin should be installed __the same way as eslint:__ 12 | 13 | ```shell 14 | $ npm i eslint -D # local 15 | $ npm i eslint-plugin-vuefix -D # local 16 | # or 17 | $ npm i eslint -g # global 18 | $ npm i eslint-plugin-vuefix -g # global 19 | ``` 20 | 21 | Example: 22 | 23 | ```json 24 | { 25 | "plugins": [ 26 | "vuefix" 27 | ], 28 | "rules": { 29 | "vuefix/vuefix": [2, {"auto": true}] 30 | } 31 | } 32 | ``` 33 | 34 | ## Options 35 | 36 | - __auto__ (Boolean, default: true). If set to false, the file will not be overwritten automatically 37 | 38 | ## A Helper Tool 39 | If you create a new .vue file, and just type 'vue' to save, this plugin will generate an sample code for your vue component. 40 | 41 | ## License 42 | 43 | [MIT](LICENSE) © qintx 44 | 45 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const vueProcessor = require('./processors/vueProcessor') 4 | const rule = require('./rules/vuefixRule') 5 | 6 | module.exports.rules = { 7 | 'vuefix': rule 8 | } 9 | 10 | module.exports.processors = { 11 | '.vue': vueProcessor 12 | } 13 | -------------------------------------------------------------------------------- /lib/options.js: -------------------------------------------------------------------------------- 1 | let options = null 2 | 3 | module.exports = { 4 | set: function(opts) { 5 | options = Object.assign({auto: true, init: true}, opts) 6 | return options 7 | }, 8 | get: function() { 9 | return options 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/processors/fixer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview An object that caches and applies source code fixes. 3 | * @author Nicholas C. Zakas 4 | */ 5 | "use strict"; 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | var debug = function(msg) { 12 | console.log(msg); 13 | }; 14 | 15 | //------------------------------------------------------------------------------ 16 | // Helpers 17 | //------------------------------------------------------------------------------ 18 | 19 | var BOM = "\uFEFF"; 20 | 21 | /** 22 | * Compares items in a messages array by line and column. 23 | * @param {Message} a The first message. 24 | * @param {Message} b The second message. 25 | * @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal. 26 | * @private 27 | */ 28 | function compareMessagesByLocation(a, b) { 29 | var lineDiff = a.line - b.line; 30 | 31 | if (lineDiff === 0) { 32 | return a.column - b.column; 33 | } else { 34 | return lineDiff; 35 | } 36 | } 37 | 38 | //------------------------------------------------------------------------------ 39 | // Public Interface 40 | //------------------------------------------------------------------------------ 41 | 42 | /** 43 | * Utility for apply fixes to source code. 44 | * @constructor 45 | */ 46 | function SourceCodeFixer() { 47 | Object.freeze(this); 48 | } 49 | 50 | /** 51 | * Applies the fixes specified by the messages to the given text. Tries to be 52 | * smart about the fixes and won't apply fixes over the same area in the text. 53 | * @param {SourceCode} sourceCode The source code to apply the changes to. 54 | * @param {Message[]} messages The array of messages reported by ESLint. 55 | * @returns {Object} An object containing the fixed text and any unfixed messages. 56 | */ 57 | SourceCodeFixer.applyFixes = function(sourceCode, messages) { 58 | 59 | // debug("Applying fixes"); 60 | 61 | if (!sourceCode) { 62 | // debug("No source code to fix"); 63 | return { 64 | fixed: false, 65 | messages: messages, 66 | output: "" 67 | }; 68 | } 69 | 70 | // clone the array 71 | var remainingMessages = [], 72 | fixes = [], 73 | text = sourceCode.text, 74 | lastFixPos = text.length + 1, 75 | prefix = (sourceCode.hasBOM ? BOM : ""); 76 | 77 | messages.forEach(function(problem) { 78 | if (problem.hasOwnProperty("fix")) { 79 | fixes.push(problem); 80 | } else { 81 | remainingMessages.push(problem); 82 | } 83 | }); 84 | 85 | if (fixes.length) { 86 | debug("Found fixes to apply"); 87 | 88 | // sort in reverse order of occurrence 89 | fixes.sort(function(a, b) { 90 | return b.fix.range[1] - a.fix.range[1] || b.fix.range[0] - a.fix.range[0]; 91 | }); 92 | 93 | // split into array of characters for easier manipulation 94 | var chars = text.split(""); 95 | 96 | fixes.forEach(function(problem) { 97 | var fix = problem.fix; 98 | var start = fix.range[0]; 99 | var end = fix.range[1]; 100 | var insertionText = fix.text; 101 | 102 | if (end < lastFixPos) { 103 | if (start < 0) { 104 | 105 | // Remove BOM. 106 | prefix = ""; 107 | start = 0; 108 | } 109 | 110 | if (start === 0 && insertionText[0] === BOM) { 111 | 112 | // Set BOM. 113 | prefix = BOM; 114 | insertionText = insertionText.slice(1); 115 | } 116 | 117 | chars.splice(start, end - start, insertionText); 118 | lastFixPos = start; 119 | } else { 120 | remainingMessages.push(problem); 121 | } 122 | }); 123 | 124 | return { 125 | fixed: true, 126 | messages: remainingMessages.sort(compareMessagesByLocation), 127 | output: prefix + chars.join("") 128 | }; 129 | } else { 130 | debug("No fixes to apply"); 131 | return { 132 | fixed: false, 133 | messages: messages, 134 | output: prefix + text 135 | }; 136 | } 137 | }; 138 | 139 | module.exports = SourceCodeFixer; 140 | -------------------------------------------------------------------------------- /lib/processors/vueProcessor.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const parse5 = require('parse5') 4 | const fs = require('fs') 5 | const options = require('../options') 6 | let offsetLine = 0 7 | 8 | // let fullOffset = 0 9 | // let fullOffsetChars = 0 10 | 11 | let startPart = '', endPart = '' 12 | let scriptContent = '' 13 | let fixer = require('./fixer') 14 | 15 | module.exports = { 16 | preprocess: function (text, filename) { 17 | startPart = '' 18 | endPart = '' 19 | let content = fs.readFileSync(filename, 'utf-8') 20 | 21 | // caculate the real line number in different IDEs 22 | const fragments = parse5.parseFragment(text, { 23 | locationInfo: true 24 | }).childNodes 25 | 26 | for (let i = 0; i < fragments.length; i++) { 27 | let frag = fragments[i] 28 | if (frag.tagName === 'script') { 29 | let location = frag.__location 30 | offsetLine = location.line - 1 31 | } 32 | } 33 | 34 | // save content of file chunks 35 | const fileInfo = parse5.parseFragment(content, { 36 | locationInfo: true 37 | }).childNodes 38 | 39 | for (let i = 0; i < fileInfo.length; i++) { 40 | let frag = fileInfo[i] 41 | if (frag.tagName === 'script') { 42 | let location = frag.__location 43 | scriptContent = frag.childNodes[0].value 44 | startPart = content.substring(0, location.startTag.endOffset) 45 | endPart = content.substring(location.endTag.startOffset) 46 | return [frag.childNodes[0].value] 47 | } 48 | } 49 | 50 | return [''] 51 | }, 52 | postprocess: function (messages, filename) { 53 | const config = options.get() 54 | var m0 = messages[0] 55 | 56 | if (!config || config.auto) { 57 | let oldLen = m0.length 58 | if (oldLen > 0) { 59 | let res = fixer.applyFixes({ 60 | text: scriptContent 61 | }, m0) 62 | 63 | if (res.fixed && res.messages.length !== oldLen) { 64 | m0 = res.messages 65 | fs.writeFileSync(filename, startPart + res.output + endPart, 'utf-8') 66 | } 67 | } 68 | } 69 | 70 | m0 && m0.forEach(function(m) { 71 | m.line += offsetLine 72 | m.endLine = m.endLine ? m.endLine + offsetLine : m.line 73 | }) 74 | 75 | // clear cache 76 | startPart = '' 77 | endPart = '' 78 | scriptContent = '' 79 | 80 | return m0 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/rules/vuefixRule.js: -------------------------------------------------------------------------------- 1 | /** 2 | * create default js code block for vue when use config: 3 | * 'rules': {"vuefix/vuefix": [{init: true}]} 4 | */ 5 | const options = require('../options') 6 | 7 | const INIT_CONTENT = [ 8 | 'export default {', 9 | ' data: () => ({', 10 | ' title: \'title\'', 11 | ' }),', 12 | ' props: {},', 13 | ' computed: {},', 14 | ' methods: {', 15 | ' hello () {', 16 | ' // your business', 17 | ' }', 18 | ' },', 19 | ' components: {}', 20 | '}' 21 | ] 22 | 23 | module.exports = { 24 | create: function(context) { 25 | let config = options.set(context.options[0] || {}) 26 | 27 | return { 28 | Program: function checkEmpty(node) { 29 | if (!config.init) { 30 | return 31 | } 32 | 33 | const sourceCode = context.getSourceCode(), 34 | src = sourceCode.getText() 35 | 36 | if (src && src.trim().toLowerCase() === 'vue') { 37 | const location = { 38 | column: node.loc.start.column, 39 | line: node.loc.start.line 40 | } 41 | 42 | // code block indent 43 | let indent = src.replace(/vue\s+/, '') 44 | let range = node.body[0] 45 | 46 | context.report({ 47 | node, 48 | loc: location, 49 | message: 'auto generate vue block', 50 | fix: function(fixer) { 51 | return fixer.replaceTextRange([range.start, range.end], INIT_CONTENT.map(function(line) { 52 | return indent + line 53 | }).join('')) 54 | } 55 | }) 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-vuefix", 3 | "version": "0.2.1", 4 | "description": "eslint plugin for autofix of .vue file", 5 | "main": "lib/index.js", 6 | "dependencies": { 7 | "parse5": "^2.1.5" 8 | }, 9 | "peerDependencies": { 10 | "eslint": ">=0.8.0" 11 | }, 12 | "devDependencies": {}, 13 | "scripts": { 14 | "test": "echo \"Error: no test specified\" && exit 1" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/lkiarest/eslint-plugin-vuefix.git" 19 | }, 20 | "keywords": [ 21 | "eslint", 22 | "eslintplugin", 23 | "eslint-plugin", 24 | "eslint-vue", 25 | "vue", 26 | "vuefix", 27 | "autofix", 28 | "linter" 29 | ], 30 | "author": "qtx", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/lkiarest/eslint-plugin-vuefix/issues" 34 | }, 35 | "homepage": "https://github.com/lkiarest/eslint-plugin-vuefix#readme" 36 | } 37 | --------------------------------------------------------------------------------