├── .eslintignore
├── .eslintrc.yml
├── .gitattributes
├── .gitignore
├── .npmrc
├── .travis.yml
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── eslint-rules
├── .eslintrc.yml
└── require-noopener.js
├── package.json
├── scripts
├── deploy.js
└── make-versions.js
├── src
├── actions
│ ├── edit-code.js
│ ├── index.js
│ ├── select-indent-size.js
│ ├── select-indent-type.js
│ ├── select-parser.js
│ └── select-rule-severity.js
├── app-footer.vue
├── app-header.vue
├── app.vue
├── appcache-toast.vue
├── configuration-category-header.vue
├── configuration-category-item.vue
├── configuration-category.vue
├── configuration-parser-select.vue
├── configuration-rules-select.vue
├── configuration.vue
├── index.html
├── index.js
├── lib
│ ├── clone.js
│ ├── eslint.js
│ ├── get-rule-url.js
│ └── rule-categories.js
├── md-icon.vue
├── message-list.vue
├── playground.vue
├── state
│ ├── default-code.js
│ ├── default-config.js
│ ├── deserialize.js
│ ├── index.js
│ ├── initial-state.js
│ └── serialize.js
└── versions.js
└── webpack.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /src/core-rules.js
3 | /src/vue-rules.js
4 | /test.*
5 |
--------------------------------------------------------------------------------
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | root: true
2 | extends:
3 | - plugin:@mysticatea/es2018
4 | - plugin:@mysticatea/+modules
5 | - plugin:@mysticatea/+browser
6 |
7 | rules:
8 | no-trailing-spaces: error
9 | require-unicode-regexp: "off"
10 |
11 | overrides:
12 | - files: "*.vue"
13 | rules:
14 | "@mysticatea/vue/component-name-in-template-casing": [error, kebab-case]
15 | require-noopener: error
16 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /node_modules
3 | /npm-debug.log
4 | /test.*
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | branches:
4 | only:
5 | - master
6 |
7 | language: node_js
8 | node_js: "10"
9 |
10 | script:
11 | - git config --local user.name "Travis CI" && npm run deploy
12 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | {"autoFix": true, "language": "vue"}
5 | ],
6 | "eslint.options": {
7 | "rulePaths": ["eslint-rules"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Toru Nagashima
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 | # vue-eslint-demo
2 |
3 | The online demo to check [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue#readme).
4 |
5 | ## ⤴️ Motivation
6 |
7 | - Let's make the reproduce code/config of bugs. It would help issue handling.
8 |
9 | ## 💿 Installation
10 |
11 | Open https://mysticatea.github.io/vue-eslint-demo/
12 |
13 | - It does not support browsers which don't support ES2015.
14 |
15 | ## 📖 Usage
16 |
17 | - Choose rules in sidebar to enable.
18 | - Input code the center area.
19 | - Right area shows the result of `eslint --fix`.
20 | - Bottom area shows the error list.
21 |
22 | ## 📰 Changelog
23 |
24 | - [GitHub Releases](https://github.com/mysticatea/vue-eslint-demo/releases)
25 |
26 | ## 🍻 Contributing
27 |
28 | Welcome contributing!
29 |
30 | Please use GitHub's Issues/PRs.
31 |
32 | ### Development Tools
33 |
34 | - `npm run build` builds the app into `/dist` directory.
35 | - `npm run clean` removes `/dist` directory.
36 | - `npm run deploy` builds the app, then updates `gh-pages` branch, then pushes it to GitHub.
37 | - `npm run update-deps` installs the latest `eslint`, `eslint-plugin-vue`, and `vue-eslint-parser`.
38 | - `npm run watch` runs the app with `webpack-dev-server`.
39 |
--------------------------------------------------------------------------------
/eslint-rules/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | root: true
2 | extends:
3 | - plugin:@mysticatea/es2018
4 | - plugin:@mysticatea/+eslint-plugin
5 |
--------------------------------------------------------------------------------
/eslint-rules/require-noopener.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | module.exports = context =>
4 | context.parserServices.defineTemplateBodyVisitor({
5 | "VElement[name='a']"(node) {
6 | const attributes = node.startTag.attributes
7 | const hasTargetBlank = attributes.some(
8 | attribute =>
9 | !attribute.directive &&
10 | attribute.key.name === "target" &&
11 | attribute.value != null &&
12 | attribute.value.value === "_blank",
13 | )
14 | const hasRelNoopener = attributes.some(
15 | attribute =>
16 | !attribute.directive &&
17 | attribute.key.name === "rel" &&
18 | attribute.value != null &&
19 | attribute.value.value === "noopener",
20 | )
21 |
22 | if (hasTargetBlank && !hasRelNoopener) {
23 | context.report({
24 | node: node.startTag,
25 | message: "Use 'rel=\"noopener\"' to open new tab.",
26 | *fix(fixer) {
27 | const lastAttribute = attributes[attributes.length - 1]
28 | yield fixer.insertTextAfter(
29 | lastAttribute,
30 | ' rel="noopener"',
31 | )
32 | },
33 | })
34 | }
35 | },
36 | })
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-eslint-demo",
3 | "version": "1.8.0",
4 | "description": "The online demo to check `eslint-plugin-vue`.",
5 | "engines": {
6 | "node": ">=8"
7 | },
8 | "files": [
9 | "dist"
10 | ],
11 | "dependencies": {},
12 | "devDependencies": {
13 | "@babel/core": "^7.1.2",
14 | "@babel/plugin-syntax-dynamic-import": "^7.0.0",
15 | "@babel/plugin-transform-runtime": "^7.1.0",
16 | "@babel/polyfill": "^7.0.0",
17 | "@babel/preset-env": "^7.1.0",
18 | "@babel/runtime": "^7.1.2",
19 | "@mysticatea/eslint-plugin": "^6.0.0",
20 | "appcache-manifest": "^2.1.0",
21 | "babel-eslint": "^10.0.1",
22 | "babel-loader": "^8.0.4",
23 | "cpx": "^1.5.0",
24 | "cross-env": "^5.1.1",
25 | "css-loader": "^1.0.0",
26 | "eslint": "^5.7.0",
27 | "eslint-plugin-vue": "^5.0.0-beta.3",
28 | "eslint4b": "^5.7.0",
29 | "file-loader": "^2.0.0",
30 | "fs-extra": "^7.0.0",
31 | "material-design-icons": "^3.0.1",
32 | "npm-run-all": "^4.1.2",
33 | "pako": "^1.0.6",
34 | "postcss-loader": "^3.0.0",
35 | "postcss-preset-env": "^6.1.2",
36 | "rimraf": "^2.6.2",
37 | "string-replace-loader": "^1.3.0",
38 | "typescript": "~3.1.6",
39 | "typescript-eslint-parser": "^21.0.1",
40 | "url-loader": "^1.1.2",
41 | "vue": "^2.5.9",
42 | "vue-eslint-editor": "^0.1.2",
43 | "vue-eslint-parser": "^3.2.2",
44 | "vue-loader": "^15.4.2",
45 | "vue-template-compiler": "^2.5.9",
46 | "webpack": "^4.21.0",
47 | "webpack-cli": "^3.1.2",
48 | "webpack-dev-server": "^3.1.9"
49 | },
50 | "scripts": {
51 | "build": "run-s -s clean build:*",
52 | "build:html": "appcache-manifest-fixer src/index.html -o dist/index.html",
53 | "build:js": "webpack --env.production --progress",
54 | "build:manifest": "appcache-manifest \"dist/**/*.{css,html,js,svg,eot,ttf,woff,woff2}\" --prefix /vue-eslint-demo --network-star -o dist/index.appcache",
55 | "build:versions": "node scripts/make-versions",
56 | "clean": "rimraf dist",
57 | "deploy": "node scripts/deploy",
58 | "lint": "eslint . --ext .js,.vue --rulesdir eslint-rules",
59 | "preversion": "run-s -s lint build",
60 | "postversion": "git push --tags && git push",
61 | "update-deps": "npm install eslint@latest eslint-plugin-vue@latest vue-eslint-parser@latest babel-eslint@latest",
62 | "watch": "run-p -s watch:*",
63 | "watch:html": "cpx src/index.html dist --watch",
64 | "watch:js": "webpack-dev-server --open --hot"
65 | },
66 | "repository": {
67 | "type": "git",
68 | "url": "git+https://github.com/mysticatea/vue-eslint-demo.git"
69 | },
70 | "keywords": [],
71 | "author": "Toru Nagashima (https://github.com/mysticatea)",
72 | "license": "MIT",
73 | "bugs": {
74 | "url": "https://github.com/mysticatea/vue-eslint-demo/issues"
75 | },
76 | "homepage": "https://github.com/mysticatea/vue-eslint-demo#readme"
77 | }
78 |
--------------------------------------------------------------------------------
/scripts/deploy.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | const { spawn } = require("child_process")
4 | const path = require("path")
5 | const fs = require("fs-extra")
6 | const version = require("../package.json").version
7 | const ATOKEN = process.env.ATOKEN
8 | const BUILD_ROOT = path.resolve(__dirname, "../dist")
9 | const DEPLOY_ROOT = path.resolve(__dirname, "..")
10 |
11 | /**
12 | * Execute a command.
13 | * @param {string} command The command to execute.
14 | * @returns {void}
15 | */
16 | function exec(command) {
17 | console.log(`> ${command}`)
18 | return new Promise((resolve, reject) => {
19 | const cp = spawn(command, [], { shell: true, stdio: "inherit" })
20 |
21 | cp.on("close", code => {
22 | if (code) {
23 | reject(new Error(`Exited with ${code}.`))
24 | } else {
25 | resolve()
26 | }
27 | })
28 | })
29 | }
30 |
31 | // Main
32 | ;(async () => {
33 | console.log("BUILD")
34 | await exec(`git checkout v${version}`)
35 | await exec("npm run -s build")
36 |
37 | console.log("LOAD GH-PAGES")
38 | if (ATOKEN) {
39 | await exec(
40 | "git fetch --depth=1 https://github.com/mysticatea/vue-eslint-demo.git gh-pages:gh-pages",
41 | )
42 | await exec("git checkout gh-pages")
43 | } else {
44 | await exec("git checkout gh-pages")
45 | await exec("git pull")
46 | }
47 |
48 | console.log("CHECK VERSIONS")
49 | const oldVersions = await fs.readFile("versions.json", "utf8")
50 | const newVersions = await fs.readFile("dist/versions.json", "utf8")
51 | console.log(`OLD: ${oldVersions}`)
52 | console.log(`NEW: ${newVersions}`)
53 |
54 | // Deploy.
55 | if (newVersions !== oldVersions) {
56 | console.log("CLEAN")
57 | for (const filename of await fs.readdir(DEPLOY_ROOT)) {
58 | const stat = await fs.stat(filename)
59 | if (!stat.isFile() || filename.startsWith(".")) {
60 | continue
61 | }
62 |
63 | console.log(`> rm ${filename}`)
64 | await fs.unlink(filename)
65 | }
66 |
67 | console.log("DEPLOY")
68 | for (const filename of await fs.readdir(BUILD_ROOT)) {
69 | console.log(`> mv dist/${filename} ${filename}`)
70 | await fs.rename(
71 | path.join(BUILD_ROOT, filename),
72 | path.join(DEPLOY_ROOT, filename),
73 | )
74 | }
75 |
76 | await exec("git add -A")
77 | let updated = false
78 | try {
79 | await exec('git commit -m "Update: website"')
80 | updated = true
81 | } catch (_error) {
82 | console.log("NO UPDATE")
83 | }
84 | if (updated) {
85 | await exec(
86 | `git push${
87 | ATOKEN
88 | ? ` https://mysticatea:${ATOKEN}@github.com/mysticatea/vue-eslint-demo.git gh-pages:gh-pages`
89 | : ""
90 | }`,
91 | )
92 | }
93 | } else {
94 | console.log("NO UPDATE")
95 | }
96 |
97 | // Back to master.
98 | await exec("git checkout master")
99 | console.log("COMPLETE")
100 | })().catch(error => {
101 | console.error(error.stack)
102 | process.exitCode = 1
103 | })
104 |
--------------------------------------------------------------------------------
/scripts/make-versions.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | const fs = require("fs")
4 |
5 | fs.writeFileSync(
6 | "dist/versions.json",
7 | JSON.stringify({
8 | "babel-eslint": require("babel-eslint/package.json").version,
9 | eslint: require("eslint/package.json").version,
10 | "eslint-plugin-vue": require("eslint-plugin-vue/package.json").version,
11 | typescript: require("typescript/package.json").version,
12 | "typescript-eslint-parser": require("typescript-eslint-parser/package.json")
13 | .version,
14 | "vue-eslint-demo": require("../package.json").version,
15 | "vue-eslint-parser": require("vue-eslint-parser/package.json").version,
16 | }),
17 | )
18 |
--------------------------------------------------------------------------------
/src/actions/edit-code.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Edit code.
3 | * @param {State} state The previous state
4 | * @param {string} code The new code.
5 | * @returns {State} The next state.
6 | */
7 | export function editCode(state, code) {
8 | state.code = code
9 | }
10 |
--------------------------------------------------------------------------------
/src/actions/index.js:
--------------------------------------------------------------------------------
1 | export * from "./edit-code"
2 | export * from "./select-indent-size"
3 | export * from "./select-indent-type"
4 | export * from "./select-parser"
5 | export * from "./select-rule-severity"
6 |
--------------------------------------------------------------------------------
/src/actions/select-indent-size.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Select indent size.
3 | * @param {State} state The previous state
4 | * @param {number} indentSize The new indent size.
5 | * @returns {State} The next state.
6 | */
7 | export function selectIndentSize(state, indentSize) {
8 | state.indentSize = indentSize
9 | }
10 |
--------------------------------------------------------------------------------
/src/actions/select-indent-type.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Select indent type.
3 | * @param {State} state The previous state
4 | * @param {string} indentType The new indent type.
5 | * @returns {State} The next state.
6 | */
7 | export function selectIndentType(state, indentType) {
8 | state.indentType = indentType
9 | }
10 |
--------------------------------------------------------------------------------
/src/actions/select-parser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Select parser.
3 | * @param {State} state The previous state
4 | * @param {string} parserId The new parser ID.
5 | * @returns {State} The next state.
6 | */
7 | export function selectParser(state, parserId) {
8 | state.config.parserOptions.parser = parserId
9 | }
10 |
--------------------------------------------------------------------------------
/src/actions/select-rule-severity.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Select rule's severity.
3 | * @param {State} state The previous state
4 | * @param {string|string[]} ruleIdOrRuleIds The rule ID to update severity.
5 | * @param {number} severity The new severity.
6 | * @returns {State} The next state.
7 | */
8 | export function selectRuleSeverity(state, ruleIdOrRuleIds, severity) {
9 | const severityMap = state.config.rules
10 |
11 | if (Array.isArray(ruleIdOrRuleIds)) {
12 | for (const ruleId of ruleIdOrRuleIds) {
13 | severityMap[ruleId] = severity
14 | }
15 | } else {
16 | const ruleId = ruleIdOrRuleIds
17 | severityMap[ruleId] = severity
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/app-footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
28 |
29 |
45 |
--------------------------------------------------------------------------------
/src/app-header.vue:
--------------------------------------------------------------------------------
1 |
2 |
32 |
33 |
34 |
77 |
78 |
103 |
--------------------------------------------------------------------------------
/src/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
89 |
90 |
134 |
--------------------------------------------------------------------------------
/src/appcache-toast.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
New version available
4 |
7 |
8 |
9 |
10 |
26 |
27 |
61 |
--------------------------------------------------------------------------------
/src/configuration-category-header.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
59 |
60 |
87 |
--------------------------------------------------------------------------------
/src/configuration-category-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
21 |
22 |
23 |
65 |
66 |
101 |
--------------------------------------------------------------------------------
/src/configuration-category.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
29 |
30 |
31 |
32 |
94 |
95 |
109 |
--------------------------------------------------------------------------------
/src/configuration-parser-select.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
67 |
--------------------------------------------------------------------------------
/src/configuration-rules-select.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
82 |
--------------------------------------------------------------------------------
/src/configuration.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
41 |
42 |
51 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Vue.js ESLint Demo
7 |
8 |
114 |
115 |
116 |
117 |
120 |
121 |
126 |
127 | Now loading...
128 |
129 |
130 |
160 |
161 |
164 |
165 |
166 |
167 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import "@babel/polyfill"
2 | import Vue from "vue"
3 | import App from "./app.vue"
4 |
5 | const app = new Vue({
6 | el: "#main",
7 | render: h => h(App, { ref: "app" }),
8 | mounted() {
9 | window.MainContent.show()
10 | },
11 | methods: {
12 | showUpdateReadyToast() {
13 | this.$refs.app.showUpdateReadyToast = true
14 | },
15 | },
16 | })
17 |
18 | // Check update.
19 | window.applicationCache.addEventListener("updateready", () => {
20 | app.showUpdateReadyToast()
21 | })
22 |
--------------------------------------------------------------------------------
/src/lib/clone.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Clone a given value deeply.
3 | * @param {any} x The value to clone.
4 | * @returns {any} The cloned value.
5 | */
6 | export function clone(x) {
7 | if (typeof x !== "object" || x === null) {
8 | return x
9 | }
10 | if (Array.isArray(x)) {
11 | return x.slice()
12 | }
13 |
14 | const y = {}
15 | for (const key of Object.keys(x)) {
16 | if (key !== "__proto__") {
17 | y[key] = clone(x[key])
18 | }
19 | }
20 | return y
21 | }
22 |
--------------------------------------------------------------------------------
/src/lib/eslint.js:
--------------------------------------------------------------------------------
1 | import Linter from "eslint4b"
2 | import plugin from "eslint-plugin-vue"
3 | import * as parser from "vue-eslint-parser"
4 |
5 | const verifyOptions = Object.freeze({
6 | preprocess: plugin.processors[".vue"].preprocess,
7 | postprocess: plugin.processors[".vue"].postprocess,
8 | })
9 |
10 | export const linter = new class extends Linter {
11 | /** Initialize this linter. */
12 | constructor() {
13 | super()
14 | this.defineParser("vue-eslint-parser", parser)
15 | for (const name of Object.keys(plugin.rules)) {
16 | this.defineRule(`vue/${name}`, plugin.rules[name])
17 | }
18 | }
19 |
20 | /** @inheritdoc */
21 | verify(textOrSourceCode, config, options) {
22 | return super.verify(textOrSourceCode, config, {
23 | ...options,
24 | ...verifyOptions,
25 | })
26 | }
27 |
28 | /** @inheritdoc */
29 | verifyAndFix(text, config, options) {
30 | return super.verifyAndFix(text, config, {
31 | ...options,
32 | ...verifyOptions,
33 | })
34 | }
35 | }()
36 |
--------------------------------------------------------------------------------
/src/lib/get-rule-url.js:
--------------------------------------------------------------------------------
1 | import { linter } from "./eslint"
2 |
3 | /**
4 | * Get the document URL of a rule.
5 | * @param {string} ruleId The rule ID to get.
6 | * @returns {string|null} The document URL of the rule.
7 | */
8 | export function getRuleUrl(ruleId) {
9 | const rule = linter.getRules().get(ruleId)
10 | const meta = rule && rule.meta
11 | const docs = meta && meta.docs
12 | const url = docs && docs.url
13 | return url || null
14 | }
15 |
--------------------------------------------------------------------------------
/src/lib/rule-categories.js:
--------------------------------------------------------------------------------
1 | import { linter } from "./eslint"
2 |
3 | // Initialize the categories of rules.
4 | /** @type {{name:string,rules:{name:string,description:string,fixable:boolean}[]}[]} */
5 | export const ruleCategories = (() => {
6 | const ruleMap = linter.getRules()
7 | const categoryMap = {
8 | essential: {
9 | name: "Priority A: Essential",
10 | rules: [],
11 | },
12 | "strongly-recommended": {
13 | name: "Priority B: Strongly Recommended",
14 | rules: [],
15 | },
16 | recommended: {
17 | name: "Priority C: Recommended",
18 | rules: [],
19 | },
20 | "use-with-caution": {
21 | name: "Priority D: Use with Caution",
22 | rules: [],
23 | },
24 | uncategorized: {
25 | name: "Uncategorized",
26 | rules: [],
27 | },
28 | base: {
29 | name: "Base Rules",
30 | rules: [],
31 | },
32 | core: {
33 | name: "ESLint Core Rules",
34 | rules: [],
35 | },
36 | }
37 | for (const entry of ruleMap) {
38 | const name = entry[0]
39 | const meta = entry[1].meta
40 | if (meta == null || meta.docs == null || meta.deprecated) {
41 | continue
42 | }
43 | const category = name.startsWith("vue/")
44 | ? categoryMap[meta.docs.category] || categoryMap.uncategorized
45 | : categoryMap.core
46 |
47 | category.rules.push({
48 | name,
49 | description: meta.docs.description || "no description",
50 | url: meta.docs.url,
51 | fixable: Boolean(meta.fixable),
52 | })
53 | }
54 |
55 | return Object.keys(categoryMap).map(id => categoryMap[id])
56 | })()
57 |
--------------------------------------------------------------------------------
/src/md-icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ props.kind }}
4 |
5 |
6 |
7 |
22 |
23 |
31 |
--------------------------------------------------------------------------------
/src/message-list.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 | -
9 | No errors.
10 |
11 | -
16 |
17 | {{ m.line }}:{{ m.column }}{{ space(m) }}{{ m.message }} (
23 | {{ m.ruleId }}
24 |
25 | FATAL
26 | )
27 |
28 |
29 |
30 |
31 |
59 |
60 |
79 |
--------------------------------------------------------------------------------
/src/playground.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
18 |
19 |
20 |
21 |
112 |
113 |
129 |
--------------------------------------------------------------------------------
/src/state/default-code.js:
--------------------------------------------------------------------------------
1 | export default `
2 |
3 |
4 | Hello, {{ name }}!
5 |
9 | {{ item.name}}
10 |
11 |
12 |
13 |
14 |
32 | `.trim()
33 |
--------------------------------------------------------------------------------
/src/state/default-config.js:
--------------------------------------------------------------------------------
1 | import { linter } from "../lib/eslint.js"
2 |
3 | export default Object.freeze({
4 | globals: Object.freeze({
5 | Array: false,
6 | ArrayBuffer: false,
7 | Boolean: false,
8 | constructor: false,
9 | DataView: false,
10 | Date: false,
11 | decodeURI: false,
12 | decodeURIComponent: false,
13 | encodeURI: false,
14 | encodeURIComponent: false,
15 | Error: false,
16 | escape: false,
17 | eval: false,
18 | EvalError: false,
19 | Float32Array: false,
20 | Float64Array: false,
21 | Function: false,
22 | hasOwnProperty: false,
23 | Infinity: false,
24 | Int16Array: false,
25 | Int32Array: false,
26 | Int8Array: false,
27 | isFinite: false,
28 | isNaN: false,
29 | isPrototypeOf: false,
30 | JSON: false,
31 | Map: false,
32 | Math: false,
33 | NaN: false,
34 | Number: false,
35 | Object: false,
36 | parseFloat: false,
37 | parseInt: false,
38 | Promise: false,
39 | propertyIsEnumerable: false,
40 | Proxy: false,
41 | RangeError: false,
42 | ReferenceError: false,
43 | Reflect: false,
44 | RegExp: false,
45 | Set: false,
46 | String: false,
47 | Symbol: false,
48 | SyntaxError: false,
49 | toLocaleString: false,
50 | toString: false,
51 | TypeError: false,
52 | Uint16Array: false,
53 | Uint32Array: false,
54 | Uint8Array: false,
55 | Uint8ClampedArray: false,
56 | undefined: false,
57 | unescape: false,
58 | URIError: false,
59 | valueOf: false,
60 | WeakMap: false,
61 | WeakSet: false,
62 | AutocompleteErrorEvent: false,
63 | CDATASection: false,
64 | ClientRect: false,
65 | ClientRectList: false,
66 | CSSAnimation: false,
67 | CSSTransition: false,
68 | CSSUnknownRule: false,
69 | CSSViewportRule: false,
70 | Debug: false,
71 | DocumentTimeline: false,
72 | DOMSettableTokenList: false,
73 | ElementTimeControl: false,
74 | FederatedCredential: false,
75 | FileError: false,
76 | HTMLAppletElement: false,
77 | HTMLBlockquoteElement: false,
78 | HTMLIsIndexElement: false,
79 | HTMLKeygenElement: false,
80 | HTMLLayerElement: false,
81 | IDBEnvironment: false,
82 | InputMethodContext: false,
83 | MediaKeyError: false,
84 | MediaKeyEvent: false,
85 | MediaKeys: false,
86 | opera: false,
87 | PasswordCredential: false,
88 | ReadableByteStream: false,
89 | SharedKeyframeList: false,
90 | showModalDialog: false,
91 | SiteBoundCredential: false,
92 | SVGAltGlyphDefElement: false,
93 | SVGAltGlyphElement: false,
94 | SVGAltGlyphItemElement: false,
95 | SVGAnimateColorElement: false,
96 | SVGAnimatedPathData: false,
97 | SVGAnimatedPoints: false,
98 | SVGColor: false,
99 | SVGColorProfileElement: false,
100 | SVGColorProfileRule: false,
101 | SVGCSSRule: false,
102 | SVGCursorElement: false,
103 | SVGDocument: false,
104 | SVGElementInstance: false,
105 | SVGElementInstanceList: false,
106 | SVGEvent: false,
107 | SVGExternalResourcesRequired: false,
108 | SVGFilterPrimitiveStandardAttributes: false,
109 | SVGFitToViewBox: false,
110 | SVGFontElement: false,
111 | SVGFontFaceElement: false,
112 | SVGFontFaceFormatElement: false,
113 | SVGFontFaceNameElement: false,
114 | SVGFontFaceSrcElement: false,
115 | SVGFontFaceUriElement: false,
116 | SVGGlyphElement: false,
117 | SVGGlyphRefElement: false,
118 | SVGHKernElement: false,
119 | SVGICCColor: false,
120 | SVGLangSpace: false,
121 | SVGLocatable: false,
122 | SVGMissingGlyphElement: false,
123 | SVGPaint: false,
124 | SVGPathSeg: false,
125 | SVGPathSegArcAbs: false,
126 | SVGPathSegArcRel: false,
127 | SVGPathSegClosePath: false,
128 | SVGPathSegCurvetoCubicAbs: false,
129 | SVGPathSegCurvetoCubicRel: false,
130 | SVGPathSegCurvetoCubicSmoothAbs: false,
131 | SVGPathSegCurvetoCubicSmoothRel: false,
132 | SVGPathSegCurvetoQuadraticAbs: false,
133 | SVGPathSegCurvetoQuadraticRel: false,
134 | SVGPathSegCurvetoQuadraticSmoothAbs: false,
135 | SVGPathSegCurvetoQuadraticSmoothRel: false,
136 | SVGPathSegLinetoAbs: false,
137 | SVGPathSegLinetoHorizontalAbs: false,
138 | SVGPathSegLinetoHorizontalRel: false,
139 | SVGPathSegLinetoRel: false,
140 | SVGPathSegLinetoVerticalAbs: false,
141 | SVGPathSegLinetoVerticalRel: false,
142 | SVGPathSegList: false,
143 | SVGPathSegMovetoAbs: false,
144 | SVGPathSegMovetoRel: false,
145 | SVGRenderingIntent: false,
146 | SVGStylable: false,
147 | SVGTests: false,
148 | SVGTransformable: false,
149 | SVGTRefElement: false,
150 | SVGURIReference: false,
151 | SVGViewSpec: false,
152 | SVGVKernElement: false,
153 | SVGZoomAndPan: false,
154 | SVGZoomEvent: false,
155 | TimeEvent: false,
156 | XDomainRequest: false,
157 | XMLHttpRequestProgressEvent: false,
158 | XPathException: false,
159 | XPathNamespace: false,
160 | XPathNSResolver: false,
161 | addEventListener: false,
162 | alert: false,
163 | AnalyserNode: false,
164 | Animation: false,
165 | AnimationEffectReadOnly: false,
166 | AnimationEffectTiming: false,
167 | AnimationEffectTimingReadOnly: false,
168 | AnimationEvent: false,
169 | AnimationPlaybackEvent: false,
170 | AnimationTimeline: false,
171 | applicationCache: false,
172 | ApplicationCache: false,
173 | ApplicationCacheErrorEvent: false,
174 | atob: false,
175 | Attr: false,
176 | Audio: false,
177 | AudioBuffer: false,
178 | AudioBufferSourceNode: false,
179 | AudioContext: false,
180 | AudioDestinationNode: false,
181 | AudioListener: false,
182 | AudioNode: false,
183 | AudioParam: false,
184 | AudioProcessingEvent: false,
185 | AudioScheduledSourceNode: false,
186 | BarProp: false,
187 | BaseAudioContext: false,
188 | BatteryManager: false,
189 | BeforeUnloadEvent: false,
190 | BiquadFilterNode: false,
191 | Blob: false,
192 | blur: false,
193 | btoa: false,
194 | BlobEvent: false,
195 | BroadcastChannel: false,
196 | BudgetService: false,
197 | ByteLengthQueuingStrategy: false,
198 | Cache: false,
199 | caches: false,
200 | CacheStorage: false,
201 | cancelAnimationFrame: false,
202 | cancelIdleCallback: false,
203 | CanvasCaptureMediaStreamTrack: false,
204 | CanvasGradient: false,
205 | CanvasPattern: false,
206 | CanvasRenderingContext2D: false,
207 | ChannelMergerNode: false,
208 | ChannelSplitterNode: false,
209 | CharacterData: false,
210 | clearInterval: false,
211 | clearTimeout: false,
212 | clientInformation: false,
213 | ClipboardEvent: false,
214 | close: false,
215 | closed: false,
216 | CloseEvent: false,
217 | Comment: false,
218 | CompositionEvent: false,
219 | confirm: false,
220 | console: false,
221 | ConstantSourceNode: false,
222 | ConvolverNode: false,
223 | createImageBitmap: false,
224 | Credential: false,
225 | CredentialsContainer: false,
226 | CountQueuingStrategy: false,
227 | Crypto: false,
228 | crypto: false,
229 | CryptoKey: false,
230 | CSS: false,
231 | CSSConditionRule: false,
232 | CSSFontFaceRule: false,
233 | CSSGroupingRule: false,
234 | CSSImportRule: false,
235 | CSSKeyframeRule: false,
236 | CSSKeyframesRule: false,
237 | CSSMediaRule: false,
238 | CSSNamespaceRule: false,
239 | CSSPageRule: false,
240 | CSSRule: false,
241 | CSSRuleList: false,
242 | CSSStyleDeclaration: false,
243 | CSSStyleRule: false,
244 | CSSStyleSheet: false,
245 | CSSSupportsRule: false,
246 | CustomElementRegistry: false,
247 | customElements: false,
248 | CustomEvent: false,
249 | DataTransfer: false,
250 | DataTransferItem: false,
251 | DataTransferItemList: false,
252 | defaultStatus: false,
253 | defaultstatus: false,
254 | DelayNode: false,
255 | DeviceMotionEvent: false,
256 | DeviceOrientationEvent: false,
257 | devicePixelRatio: false,
258 | dispatchEvent: false,
259 | document: false,
260 | Document: false,
261 | DocumentFragment: false,
262 | DocumentType: false,
263 | DOMError: false,
264 | DOMException: false,
265 | DOMImplementation: false,
266 | DOMMatrix: false,
267 | DOMMatrixReadOnly: false,
268 | DOMParser: false,
269 | DOMPoint: false,
270 | DOMPointReadOnly: false,
271 | DOMQuad: false,
272 | DOMRect: false,
273 | DOMRectReadOnly: false,
274 | DOMStringList: false,
275 | DOMStringMap: false,
276 | DOMTokenList: false,
277 | DragEvent: false,
278 | DynamicsCompressorNode: false,
279 | Element: false,
280 | ErrorEvent: false,
281 | event: false,
282 | Event: false,
283 | EventSource: false,
284 | EventTarget: false,
285 | external: false,
286 | fetch: false,
287 | File: false,
288 | FileList: false,
289 | FileReader: false,
290 | find: false,
291 | focus: false,
292 | FocusEvent: false,
293 | FontFace: false,
294 | FontFaceSetLoadEvent: false,
295 | FormData: false,
296 | frameElement: false,
297 | frames: false,
298 | GainNode: false,
299 | Gamepad: false,
300 | GamepadButton: false,
301 | GamepadEvent: false,
302 | getComputedStyle: false,
303 | getSelection: false,
304 | HashChangeEvent: false,
305 | Headers: false,
306 | history: false,
307 | History: false,
308 | HTMLAllCollection: false,
309 | HTMLAnchorElement: false,
310 | HTMLAreaElement: false,
311 | HTMLAudioElement: false,
312 | HTMLBaseElement: false,
313 | HTMLBodyElement: false,
314 | HTMLBRElement: false,
315 | HTMLButtonElement: false,
316 | HTMLCanvasElement: false,
317 | HTMLCollection: false,
318 | HTMLContentElement: false,
319 | HTMLDataElement: false,
320 | HTMLDataListElement: false,
321 | HTMLDetailsElement: false,
322 | HTMLDialogElement: false,
323 | HTMLDirectoryElement: false,
324 | HTMLDivElement: false,
325 | HTMLDListElement: false,
326 | HTMLDocument: false,
327 | HTMLElement: false,
328 | HTMLEmbedElement: false,
329 | HTMLFieldSetElement: false,
330 | HTMLFontElement: false,
331 | HTMLFormControlsCollection: false,
332 | HTMLFormElement: false,
333 | HTMLFrameElement: false,
334 | HTMLFrameSetElement: false,
335 | HTMLHeadElement: false,
336 | HTMLHeadingElement: false,
337 | HTMLHRElement: false,
338 | HTMLHtmlElement: false,
339 | HTMLIFrameElement: false,
340 | HTMLImageElement: false,
341 | HTMLInputElement: false,
342 | HTMLLabelElement: false,
343 | HTMLLegendElement: false,
344 | HTMLLIElement: false,
345 | HTMLLinkElement: false,
346 | HTMLMapElement: false,
347 | HTMLMarqueeElement: false,
348 | HTMLMediaElement: false,
349 | HTMLMenuElement: false,
350 | HTMLMetaElement: false,
351 | HTMLMeterElement: false,
352 | HTMLModElement: false,
353 | HTMLObjectElement: false,
354 | HTMLOListElement: false,
355 | HTMLOptGroupElement: false,
356 | HTMLOptionElement: false,
357 | HTMLOptionsCollection: false,
358 | HTMLOutputElement: false,
359 | HTMLParagraphElement: false,
360 | HTMLParamElement: false,
361 | HTMLPictureElement: false,
362 | HTMLPreElement: false,
363 | HTMLProgressElement: false,
364 | HTMLQuoteElement: false,
365 | HTMLScriptElement: false,
366 | HTMLSelectElement: false,
367 | HTMLShadowElement: false,
368 | HTMLSlotElement: false,
369 | HTMLSourceElement: false,
370 | HTMLSpanElement: false,
371 | HTMLStyleElement: false,
372 | HTMLTableCaptionElement: false,
373 | HTMLTableCellElement: false,
374 | HTMLTableColElement: false,
375 | HTMLTableElement: false,
376 | HTMLTableRowElement: false,
377 | HTMLTableSectionElement: false,
378 | HTMLTemplateElement: false,
379 | HTMLTextAreaElement: false,
380 | HTMLTimeElement: false,
381 | HTMLTitleElement: false,
382 | HTMLTrackElement: false,
383 | HTMLUListElement: false,
384 | HTMLUnknownElement: false,
385 | HTMLVideoElement: false,
386 | IDBCursor: false,
387 | IDBCursorWithValue: false,
388 | IDBDatabase: false,
389 | IDBFactory: false,
390 | IDBIndex: false,
391 | IDBKeyRange: false,
392 | IDBObjectStore: false,
393 | IDBOpenDBRequest: false,
394 | IDBRequest: false,
395 | IDBTransaction: false,
396 | IDBVersionChangeEvent: false,
397 | IdleDeadline: false,
398 | IIRFilterNode: false,
399 | Image: false,
400 | ImageBitmap: false,
401 | ImageBitmapRenderingContext: false,
402 | ImageCapture: false,
403 | ImageData: false,
404 | indexedDB: false,
405 | innerHeight: false,
406 | innerWidth: false,
407 | InputEvent: false,
408 | IntersectionObserver: false,
409 | IntersectionObserverEntry: false,
410 | Intl: false,
411 | isSecureContext: false,
412 | KeyboardEvent: false,
413 | KeyframeEffect: false,
414 | KeyframeEffectReadOnly: false,
415 | length: false,
416 | localStorage: false,
417 | location: false,
418 | Location: false,
419 | locationbar: false,
420 | matchMedia: false,
421 | MediaDeviceInfo: false,
422 | MediaDevices: false,
423 | MediaElementAudioSourceNode: false,
424 | MediaEncryptedEvent: false,
425 | MediaError: false,
426 | MediaKeyMessageEvent: false,
427 | MediaKeySession: false,
428 | MediaKeyStatusMap: false,
429 | MediaKeySystemAccess: false,
430 | MediaList: false,
431 | MediaQueryList: false,
432 | MediaQueryListEvent: false,
433 | MediaSource: false,
434 | MediaRecorder: false,
435 | MediaSettingsRange: false,
436 | MediaStream: false,
437 | MediaStreamAudioDestinationNode: false,
438 | MediaStreamAudioSourceNode: false,
439 | MediaStreamEvent: false,
440 | MediaStreamTrack: false,
441 | MediaStreamTrackEvent: false,
442 | menubar: false,
443 | MessageChannel: false,
444 | MessageEvent: false,
445 | MessagePort: false,
446 | MIDIAccess: false,
447 | MIDIConnectionEvent: false,
448 | MIDIInput: false,
449 | MIDIInputMap: false,
450 | MIDIMessageEvent: false,
451 | MIDIOutput: false,
452 | MIDIOutputMap: false,
453 | MIDIPort: false,
454 | MimeType: false,
455 | MimeTypeArray: false,
456 | MouseEvent: false,
457 | moveBy: false,
458 | moveTo: false,
459 | MutationEvent: false,
460 | MutationObserver: false,
461 | MutationRecord: false,
462 | name: false,
463 | NamedNodeMap: false,
464 | NavigationPreloadManager: false,
465 | navigator: false,
466 | Navigator: false,
467 | NetworkInformation: false,
468 | Node: false,
469 | NodeFilter: false,
470 | NodeIterator: false,
471 | NodeList: false,
472 | Notification: false,
473 | OfflineAudioCompletionEvent: false,
474 | OfflineAudioContext: false,
475 | offscreenBuffering: false,
476 | onabort: true,
477 | onafterprint: true,
478 | onanimationend: true,
479 | onanimationiteration: true,
480 | onanimationstart: true,
481 | onappinstalled: true,
482 | onauxclick: true,
483 | onbeforeinstallprompt: true,
484 | onbeforeprint: true,
485 | onbeforeunload: true,
486 | onblur: true,
487 | oncancel: true,
488 | oncanplay: true,
489 | oncanplaythrough: true,
490 | onchange: true,
491 | onclick: true,
492 | onclose: true,
493 | oncontextmenu: true,
494 | oncuechange: true,
495 | ondblclick: true,
496 | ondevicemotion: true,
497 | ondeviceorientation: true,
498 | ondeviceorientationabsolute: true,
499 | ondrag: true,
500 | ondragend: true,
501 | ondragenter: true,
502 | ondragleave: true,
503 | ondragover: true,
504 | ondragstart: true,
505 | ondrop: true,
506 | ondurationchange: true,
507 | onemptied: true,
508 | onended: true,
509 | onerror: true,
510 | onfocus: true,
511 | ongotpointercapture: true,
512 | onhashchange: true,
513 | oninput: true,
514 | oninvalid: true,
515 | onkeydown: true,
516 | onkeypress: true,
517 | onkeyup: true,
518 | onlanguagechange: true,
519 | onload: true,
520 | onloadeddata: true,
521 | onloadedmetadata: true,
522 | onloadstart: true,
523 | onlostpointercapture: true,
524 | onmessage: true,
525 | onmessageerror: true,
526 | onmousedown: true,
527 | onmouseenter: true,
528 | onmouseleave: true,
529 | onmousemove: true,
530 | onmouseout: true,
531 | onmouseover: true,
532 | onmouseup: true,
533 | onmousewheel: true,
534 | onoffline: true,
535 | ononline: true,
536 | onpagehide: true,
537 | onpageshow: true,
538 | onpause: true,
539 | onplay: true,
540 | onplaying: true,
541 | onpointercancel: true,
542 | onpointerdown: true,
543 | onpointerenter: true,
544 | onpointerleave: true,
545 | onpointermove: true,
546 | onpointerout: true,
547 | onpointerover: true,
548 | onpointerup: true,
549 | onpopstate: true,
550 | onprogress: true,
551 | onratechange: true,
552 | onrejectionhandled: true,
553 | onreset: true,
554 | onresize: true,
555 | onscroll: true,
556 | onsearch: true,
557 | onseeked: true,
558 | onseeking: true,
559 | onselect: true,
560 | onstalled: true,
561 | onstorage: true,
562 | onsubmit: true,
563 | onsuspend: true,
564 | ontimeupdate: true,
565 | ontoggle: true,
566 | ontransitionend: true,
567 | onunhandledrejection: true,
568 | onunload: true,
569 | onvolumechange: true,
570 | onwaiting: true,
571 | onwheel: true,
572 | open: false,
573 | openDatabase: false,
574 | opener: false,
575 | Option: false,
576 | origin: false,
577 | OscillatorNode: false,
578 | outerHeight: false,
579 | outerWidth: false,
580 | PageTransitionEvent: false,
581 | pageXOffset: false,
582 | pageYOffset: false,
583 | PannerNode: false,
584 | parent: false,
585 | Path2D: false,
586 | PaymentAddress: false,
587 | PaymentRequest: false,
588 | PaymentRequestUpdateEvent: false,
589 | PaymentResponse: false,
590 | performance: false,
591 | Performance: false,
592 | PerformanceEntry: false,
593 | PerformanceLongTaskTiming: false,
594 | PerformanceMark: false,
595 | PerformanceMeasure: false,
596 | PerformanceNavigation: false,
597 | PerformanceNavigationTiming: false,
598 | PerformanceObserver: false,
599 | PerformanceObserverEntryList: false,
600 | PerformancePaintTiming: false,
601 | PerformanceResourceTiming: false,
602 | PerformanceTiming: false,
603 | PeriodicWave: false,
604 | Permissions: false,
605 | PermissionStatus: false,
606 | personalbar: false,
607 | PhotoCapabilities: false,
608 | Plugin: false,
609 | PluginArray: false,
610 | PointerEvent: false,
611 | PopStateEvent: false,
612 | postMessage: false,
613 | Presentation: false,
614 | PresentationAvailability: false,
615 | PresentationConnection: false,
616 | PresentationConnectionAvailableEvent: false,
617 | PresentationConnectionCloseEvent: false,
618 | PresentationConnectionList: false,
619 | PresentationReceiver: false,
620 | PresentationRequest: false,
621 | print: false,
622 | ProcessingInstruction: false,
623 | ProgressEvent: false,
624 | PromiseRejectionEvent: false,
625 | prompt: false,
626 | PushManager: false,
627 | PushSubscription: false,
628 | PushSubscriptionOptions: false,
629 | RadioNodeList: false,
630 | Range: false,
631 | ReadableStream: false,
632 | removeEventListener: false,
633 | RemotePlayback: false,
634 | Request: false,
635 | requestAnimationFrame: false,
636 | requestIdleCallback: false,
637 | resizeBy: false,
638 | ResizeObserver: false,
639 | ResizeObserverEntry: false,
640 | resizeTo: false,
641 | Response: false,
642 | RTCCertificate: false,
643 | RTCDataChannel: false,
644 | RTCDataChannelEvent: false,
645 | RTCIceCandidate: false,
646 | RTCPeerConnection: false,
647 | RTCPeerConnectionIceEvent: false,
648 | RTCRtpContributingSource: false,
649 | RTCRtpReceiver: false,
650 | RTCSessionDescription: false,
651 | RTCStatsReport: false,
652 | screen: false,
653 | Screen: false,
654 | screenLeft: false,
655 | ScreenOrientation: false,
656 | screenTop: false,
657 | screenX: false,
658 | screenY: false,
659 | ScriptProcessorNode: false,
660 | scroll: false,
661 | scrollbars: false,
662 | scrollBy: false,
663 | scrollTo: false,
664 | scrollX: false,
665 | scrollY: false,
666 | SecurityPolicyViolationEvent: false,
667 | Selection: false,
668 | self: false,
669 | ServiceWorker: false,
670 | ServiceWorkerContainer: false,
671 | ServiceWorkerRegistration: false,
672 | sessionStorage: false,
673 | setInterval: false,
674 | setTimeout: false,
675 | ShadowRoot: false,
676 | SharedWorker: false,
677 | SourceBuffer: false,
678 | SourceBufferList: false,
679 | speechSynthesis: false,
680 | SpeechSynthesisEvent: false,
681 | SpeechSynthesisUtterance: false,
682 | StaticRange: false,
683 | status: false,
684 | statusbar: false,
685 | StereoPannerNode: false,
686 | stop: false,
687 | Storage: false,
688 | StorageEvent: false,
689 | StorageManager: false,
690 | styleMedia: false,
691 | StyleSheet: false,
692 | StyleSheetList: false,
693 | SubtleCrypto: false,
694 | SVGAElement: false,
695 | SVGAngle: false,
696 | SVGAnimatedAngle: false,
697 | SVGAnimatedBoolean: false,
698 | SVGAnimatedEnumeration: false,
699 | SVGAnimatedInteger: false,
700 | SVGAnimatedLength: false,
701 | SVGAnimatedLengthList: false,
702 | SVGAnimatedNumber: false,
703 | SVGAnimatedNumberList: false,
704 | SVGAnimatedPreserveAspectRatio: false,
705 | SVGAnimatedRect: false,
706 | SVGAnimatedString: false,
707 | SVGAnimatedTransformList: false,
708 | SVGAnimateElement: false,
709 | SVGAnimateMotionElement: false,
710 | SVGAnimateTransformElement: false,
711 | SVGAnimationElement: false,
712 | SVGCircleElement: false,
713 | SVGClipPathElement: false,
714 | SVGComponentTransferFunctionElement: false,
715 | SVGDefsElement: false,
716 | SVGDescElement: false,
717 | SVGDiscardElement: false,
718 | SVGElement: false,
719 | SVGEllipseElement: false,
720 | SVGFEBlendElement: false,
721 | SVGFEColorMatrixElement: false,
722 | SVGFEComponentTransferElement: false,
723 | SVGFECompositeElement: false,
724 | SVGFEConvolveMatrixElement: false,
725 | SVGFEDiffuseLightingElement: false,
726 | SVGFEDisplacementMapElement: false,
727 | SVGFEDistantLightElement: false,
728 | SVGFEDropShadowElement: false,
729 | SVGFEFloodElement: false,
730 | SVGFEFuncAElement: false,
731 | SVGFEFuncBElement: false,
732 | SVGFEFuncGElement: false,
733 | SVGFEFuncRElement: false,
734 | SVGFEGaussianBlurElement: false,
735 | SVGFEImageElement: false,
736 | SVGFEMergeElement: false,
737 | SVGFEMergeNodeElement: false,
738 | SVGFEMorphologyElement: false,
739 | SVGFEOffsetElement: false,
740 | SVGFEPointLightElement: false,
741 | SVGFESpecularLightingElement: false,
742 | SVGFESpotLightElement: false,
743 | SVGFETileElement: false,
744 | SVGFETurbulenceElement: false,
745 | SVGFilterElement: false,
746 | SVGForeignObjectElement: false,
747 | SVGGElement: false,
748 | SVGGeometryElement: false,
749 | SVGGradientElement: false,
750 | SVGGraphicsElement: false,
751 | SVGImageElement: false,
752 | SVGLength: false,
753 | SVGLengthList: false,
754 | SVGLinearGradientElement: false,
755 | SVGLineElement: false,
756 | SVGMarkerElement: false,
757 | SVGMaskElement: false,
758 | SVGMatrix: false,
759 | SVGMetadataElement: false,
760 | SVGMPathElement: false,
761 | SVGNumber: false,
762 | SVGNumberList: false,
763 | SVGPathElement: false,
764 | SVGPatternElement: false,
765 | SVGPoint: false,
766 | SVGPointList: false,
767 | SVGPolygonElement: false,
768 | SVGPolylineElement: false,
769 | SVGPreserveAspectRatio: false,
770 | SVGRadialGradientElement: false,
771 | SVGRect: false,
772 | SVGRectElement: false,
773 | SVGScriptElement: false,
774 | SVGSetElement: false,
775 | SVGStopElement: false,
776 | SVGStringList: false,
777 | SVGStyleElement: false,
778 | SVGSVGElement: false,
779 | SVGSwitchElement: false,
780 | SVGSymbolElement: false,
781 | SVGTextContentElement: false,
782 | SVGTextElement: false,
783 | SVGTextPathElement: false,
784 | SVGTextPositioningElement: false,
785 | SVGTitleElement: false,
786 | SVGTransform: false,
787 | SVGTransformList: false,
788 | SVGTSpanElement: false,
789 | SVGUnitTypes: false,
790 | SVGUseElement: false,
791 | SVGViewElement: false,
792 | TaskAttributionTiming: false,
793 | Text: false,
794 | TextDecoder: false,
795 | TextEncoder: false,
796 | TextEvent: false,
797 | TextMetrics: false,
798 | TextTrack: false,
799 | TextTrackCue: false,
800 | TextTrackCueList: false,
801 | TextTrackList: false,
802 | TimeRanges: false,
803 | toolbar: false,
804 | top: false,
805 | Touch: false,
806 | TouchEvent: false,
807 | TouchList: false,
808 | TrackEvent: false,
809 | TransitionEvent: false,
810 | TreeWalker: false,
811 | UIEvent: false,
812 | URL: false,
813 | URLSearchParams: false,
814 | ValidityState: false,
815 | VisualViewport: false,
816 | visualViewport: false,
817 | VTTCue: false,
818 | WaveShaperNode: false,
819 | WebAssembly: false,
820 | WebGL2RenderingContext: false,
821 | WebGLActiveInfo: false,
822 | WebGLBuffer: false,
823 | WebGLContextEvent: false,
824 | WebGLFramebuffer: false,
825 | WebGLProgram: false,
826 | WebGLQuery: false,
827 | WebGLRenderbuffer: false,
828 | WebGLRenderingContext: false,
829 | WebGLSampler: false,
830 | WebGLShader: false,
831 | WebGLShaderPrecisionFormat: false,
832 | WebGLSync: false,
833 | WebGLTexture: false,
834 | WebGLTransformFeedback: false,
835 | WebGLUniformLocation: false,
836 | WebGLVertexArrayObject: false,
837 | WebSocket: false,
838 | WheelEvent: false,
839 | Window: false,
840 | window: false,
841 | Worker: false,
842 | WritableStream: false,
843 | XMLDocument: false,
844 | XMLHttpRequest: false,
845 | XMLHttpRequestEventTarget: false,
846 | XMLHttpRequestUpload: false,
847 | XMLSerializer: false,
848 | XPathEvaluator: false,
849 | XPathExpression: false,
850 | XPathResult: false,
851 | XSLTProcessor: false,
852 | }),
853 |
854 | rules: Object.freeze(
855 | (() => {
856 | const rules = {}
857 | for (const [name, rule] of linter.getRules()) {
858 | const enabled =
859 | name.startsWith("vue/") ||
860 | rule.meta.docs.recommended ||
861 | name === "object-shorthand"
862 | rules[name] = enabled ? 2 : 0
863 | }
864 | return rules
865 | })(),
866 | ),
867 |
868 | parser: "vue-eslint-parser",
869 | parserOptions: Object.freeze({
870 | parser: "espree",
871 | ecmaVersion: 2019,
872 | sourceType: "module",
873 | ecmaFeatures: Object.freeze({ jsx: true }),
874 | }),
875 | })
876 |
--------------------------------------------------------------------------------
/src/state/deserialize.js:
--------------------------------------------------------------------------------
1 | import pako from "pako"
2 | import { clone } from "../lib/clone"
3 | import { initialState } from "./initial-state"
4 |
5 | /*eslint-disable complexity */
6 | /**
7 | * Deserialize a given serialized string then update this object.
8 | * @param {string} serializedString A serialized string.
9 | * @returns {object} The deserialized state.
10 | */
11 | export function deserializeState(serializedString) {
12 | const state = clone(initialState)
13 |
14 | if (serializedString === "") {
15 | return state
16 | }
17 |
18 | try {
19 | // For backward compatibility, it can address non-compressed data.
20 | const compressed = !serializedString.startsWith("eyJj")
21 | const decodedText = atob(serializedString)
22 | const jsonText = compressed
23 | ? pako.inflate(decodedText, { to: "string" })
24 | : decodedText
25 | const json = JSON.parse(jsonText)
26 |
27 | if (typeof json === "object" && json !== null) {
28 | if (typeof json.code === "string") {
29 | state.code = json.code
30 | }
31 | if (typeof json.rules === "object" && json.rules !== null) {
32 | for (const id of Object.keys(state.config.rules)) {
33 | state.config.rules[id] = json.rules[id] ? 2 : 0
34 | }
35 | }
36 | if (
37 | json.indentSize === 2 ||
38 | json.indentSize === 4 ||
39 | json.indentSize === 8
40 | ) {
41 | state.indentSize = json.indentSize
42 | }
43 | if (json.indentType === "space" || json.indentType === "tab") {
44 | state.indentType = json.indentType
45 | }
46 | if (
47 | json.parser === "espree" ||
48 | json.parser === "babel-eslint" ||
49 | json.parser === "typescript-eslint-parser"
50 | ) {
51 | state.config.parserOptions.parser = json.parser
52 | }
53 | }
54 | } catch (error) {
55 | console.error(error)
56 | }
57 |
58 | return state
59 | }
60 | /*eslint-enable complexity */
61 |
--------------------------------------------------------------------------------
/src/state/index.js:
--------------------------------------------------------------------------------
1 | export * from "./deserialize"
2 | export * from "./initial-state"
3 | export * from "./serialize"
4 |
--------------------------------------------------------------------------------
/src/state/initial-state.js:
--------------------------------------------------------------------------------
1 | import defaultCode from "./default-code"
2 | import defaultConfig from "./default-config"
3 | export * from "../lib/eslint"
4 |
5 | /**
6 | * The initial state.
7 | */
8 | export const initialState = Object.freeze({
9 | code: defaultCode,
10 | config: defaultConfig,
11 | indentType: "space",
12 | indentSize: 2,
13 | })
14 |
--------------------------------------------------------------------------------
/src/state/serialize.js:
--------------------------------------------------------------------------------
1 | import pako from "pako"
2 |
3 | /**
4 | * Get only enabled rules to make the serialized data smaller.
5 | * @param {object} allRules The rule settings.
6 | * @returns {object} The rule settings for the enabled rules.
7 | */
8 | function getEnabledRules(allRules) {
9 | return Object.keys(allRules).reduce((map, id) => {
10 | if (allRules[id] === 2) {
11 | map[id] = 2
12 | }
13 | return map
14 | }, {})
15 | }
16 |
17 | /**
18 | * Serialize a given state as a base64 string.
19 | * @param {State} state The state to serialize.
20 | * @returns {string} The serialized string.
21 | */
22 | export function serializeState(state) {
23 | const jsonString = JSON.stringify({
24 | code: state.code,
25 | rules: getEnabledRules(state.config.rules),
26 | indentSize: state.indentSize,
27 | indentType: state.indentType,
28 | parser: state.config.parserOptions.parser,
29 | })
30 | const compressedString = pako.deflate(jsonString, { to: "string" })
31 | const base64 = btoa(compressedString)
32 |
33 | //eslint-disable-next-line no-console
34 | console.log(
35 | `The compress rate of serialized string: ${(
36 | (100 * base64.length) /
37 | jsonString.length
38 | ).toFixed(1)}% (${jsonString.length}B → ${base64.length}B)`,
39 | )
40 |
41 | return base64
42 | }
43 |
--------------------------------------------------------------------------------
/src/versions.js:
--------------------------------------------------------------------------------
1 | // THE CONTENT OF THIS FILE WILL BE INJECTED IN BUILD SCRIPT.
2 | export default {
3 | "vue-eslint-demo": {
4 | repo: "mysticatea/vue-eslint-demo",
5 | version: "0.0.0",
6 | },
7 | eslint: {
8 | repo: "eslint/eslint",
9 | version: "0.0.0",
10 | },
11 | "eslint-plugin-vue": {
12 | repo: "vuejs/eslint-plugin-vue",
13 | version: "0.0.0",
14 | },
15 | "vue-eslint-parser": {
16 | repo: "mysticatea/vue-eslint-parser",
17 | version: "0.0.0",
18 | },
19 | "babel-eslint": {
20 | repo: "babel/babel-eslint",
21 | version: "0.0.0",
22 | },
23 | "typescript-eslint-parser": {
24 | repo: "eslint/typescript-eslint-parser",
25 | version: "0.0.0",
26 | },
27 | typescript: {
28 | repo: "Microsoft/typescript",
29 | version: "0.0.0",
30 | },
31 | }
32 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /*eslint-env node */
2 |
3 | const path = require("path")
4 | const postcssPresetEnv = require("postcss-preset-env")
5 | const VueLoaderPlugin = require("vue-loader/lib/plugin")
6 |
7 | // Shim for `src/versions.js`
8 | const VERSIONS = `export default ${JSON.stringify({
9 | "vue-eslint-demo": {
10 | repo: "mysticatea/vue-eslint-demo",
11 | version: require("./package.json").version,
12 | },
13 | eslint: {
14 | repo: "eslint/eslint",
15 | version: require("eslint/package.json").version,
16 | },
17 | "eslint-plugin-vue": {
18 | repo: "vuejs/eslint-plugin-vue",
19 | version: require("eslint-plugin-vue/package.json").version,
20 | },
21 | "vue-eslint-parser": {
22 | repo: "mysticatea/vue-eslint-parser",
23 | version: require("vue-eslint-parser/package.json").version,
24 | },
25 | "babel-eslint": {
26 | repo: "babel/babel-eslint",
27 | version: require("babel-eslint/package.json").version,
28 | },
29 | "typescript-eslint-parser": {
30 | repo: "eslint/typescript-eslint-parser",
31 | version: require("typescript-eslint-parser/package.json").version,
32 | },
33 | typescript: {
34 | repo: "Microsoft/typescript",
35 | version: require("typescript/package.json").version,
36 | },
37 | })}`
38 |
39 | // Shim for vue-eslint-parser.
40 | const IMPORT_PARSER = `(
41 | parserOptions.parser === "babel-eslint" ? require("babel-eslint") :
42 | parserOptions.parser === "typescript-eslint-parser" ? require("typescript-eslint-parser") :
43 | /* otherwise */ require("espree")
44 | )`
45 |
46 | module.exports = env => {
47 | const prod = Boolean(env && env.production)
48 | const mode = prod ? "production" : "development"
49 | const browserlist = [">1%", "not dead", "not ie 11"]
50 |
51 | return {
52 | mode,
53 | target: "web",
54 | entry: "./src/index.js",
55 | output: {
56 | path: path.resolve(__dirname, "./dist"),
57 | filename: "index.js",
58 | },
59 | module: {
60 | rules: [
61 | {
62 | test: /\.vue$/,
63 | use: ["vue-loader"],
64 | },
65 | {
66 | test: /\.m?js$/,
67 | exclude: /node_modules[\\/](?!vue-eslint-editor)/,
68 | use: [
69 | {
70 | loader: "babel-loader",
71 | options: {
72 | babelrc: false,
73 | cacheDirectory: true,
74 | plugins: [
75 | "@babel/plugin-syntax-dynamic-import",
76 | [
77 | "@babel/plugin-transform-runtime",
78 | { useESModules: true },
79 | ],
80 | ],
81 | presets: [
82 | [
83 | "@babel/preset-env",
84 | {
85 | modules: false,
86 | targets: browserlist,
87 | useBuiltIns: "entry",
88 | },
89 | ],
90 | ],
91 | },
92 | },
93 | ],
94 | },
95 | {
96 | test: /\.css$/,
97 | use: [
98 | {
99 | loader: "vue-style-loader",
100 | options: {},
101 | },
102 | {
103 | loader: "css-loader",
104 | options: {},
105 | },
106 | {
107 | loader: "postcss-loader",
108 | options: {
109 | plugins: [
110 | postcssPresetEnv({
111 | browsers: browserlist,
112 | stage: 3,
113 | }),
114 | ],
115 | },
116 | },
117 | ],
118 | },
119 | {
120 | test: /\.(png|jpg|gif|svg|eot|ijmap|ttf|woff2?)$/,
121 | use: [
122 | {
123 | loader: "url-loader",
124 | options: {
125 | limit: 8192,
126 | },
127 | },
128 | ],
129 | },
130 | // Replace `./src/versions.js` with the current versions.
131 | {
132 | test: /src[\\/]versions/,
133 | use: [
134 | {
135 | loader: "string-replace-loader",
136 | options: {
137 | search: "[\\s\\S]+", // whole file.
138 | replace: VERSIONS,
139 | flags: "g",
140 | },
141 | },
142 | ],
143 | },
144 | // `vue-eslint-parser` has `require(parserOptions.parser || "espree")`.
145 | // Modify it by a static importing.
146 | {
147 | test: /node_modules[/\\]vue-eslint-parser[/\\]index\.js$/,
148 | use: [
149 | {
150 | loader: "string-replace-loader",
151 | options: {
152 | search:
153 | 'typeof parserOptions.parser === "string"\n ? require(parserOptions.parser)\n : require("espree")',
154 | replace: IMPORT_PARSER,
155 | },
156 | },
157 | ],
158 | },
159 | // Patch for `babel-eslint` -- accessing `global` causes build error.
160 | {
161 | test: /node_modules[/\\]babel-eslint[/\\]lib[/\\]analyze-scope\.js$/,
162 | use: [
163 | {
164 | loader: "string-replace-loader",
165 | options: {
166 | search: 'require("./patch-eslint-scope")',
167 | replace: "Object",
168 | },
169 | },
170 | ],
171 | },
172 | // Patch for `eslint-utils` -- accessing `global` causes build error.
173 | {
174 | test: /node_modules[/\\]eslint-utils[/\\]index\.m?js$/,
175 | use: [
176 | {
177 | loader: "string-replace-loader",
178 | options: {
179 | search: "\\bin global\\b",
180 | replace: "in window",
181 | flags: "g",
182 | },
183 | },
184 | {
185 | loader: "string-replace-loader",
186 | options: {
187 | search: "\\bglobal\\[",
188 | replace: "window[",
189 | flags: "g",
190 | },
191 | },
192 | ],
193 | },
194 | // Patch for `typescript`
195 | {
196 | test: /node_modules[/\\]typescript[/\\]lib[/\\]typescript.js$/,
197 | use: [
198 | {
199 | loader: "string-replace-loader",
200 | options: {
201 | search: "require\\(.+?\\)",
202 | replace: "null",
203 | flags: "g",
204 | },
205 | },
206 | ],
207 | },
208 | ],
209 | },
210 | resolve: {
211 | alias: {
212 | vue$: "vue/dist/vue.esm.js",
213 | },
214 | extensions: [".mjs", ".js", ".vue", ".json"],
215 | },
216 | plugins: [new VueLoaderPlugin()],
217 | devServer: {
218 | contentBase: path.join(__dirname, "dist"),
219 | compress: true,
220 | },
221 | performance: {
222 | hints: false,
223 | },
224 | devtool: false,
225 | }
226 | }
227 |
--------------------------------------------------------------------------------