Skip to content

Add defineCustomBlocksVisitor to parserServices #91

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 14, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -173,6 +173,7 @@ But, it cannot be parsed with Vue 2.
- `defineTemplateBodyVisitor(templateVisitor, scriptVisitor)` ... returns ESLint visitor to traverse `<template>`.
- `getTemplateBodyTokenStore()` ... returns ESLint `TokenStore` to get the tokens of `<template>`.
- `getDocumentFragment()` ... returns the root `VDocumentFragment`.
- `defineCustomBlocksVisitor(context, customParser, rule, scriptVisitor)` ... returns ESLint visitor that parses and traverses the contents of the custom block.
- [ast.md](./docs/ast.md) is `<template>` AST specification.
- [mustache-interpolation-spacing.js](https://github.com/vuejs/eslint-plugin-vue/blob/b434ff99d37f35570fa351681e43ba2cf5746db3/lib/rules/mustache-interpolation-spacing.js) is an example.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -23,7 +23,8 @@
"devDependencies": {
"@mysticatea/eslint-plugin": "^13.0.0",
"@types/debug": "0.0.30",
"@types/estree": "0.0.38",
"@types/eslint": "^7.2.6",
"@types/estree": "0.0.45",
"@types/lodash": "^4.14.120",
"@types/mocha": "^5.2.4",
"@types/node": "^10.12.21",
@@ -35,6 +36,7 @@
"dts-bundle": "^0.7.3",
"eslint": "^7.0.0",
"fs-extra": "^7.0.1",
"jsonc-eslint-parser": "^0.6.0",
"mocha": "^6.1.4",
"npm-run-all": "^4.1.5",
"nyc": "^14.0.0",
67 changes: 67 additions & 0 deletions src/common/fix-locations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
ESLintExtendedProgram,
LocationRange,
Node,
traverseNodes,
} from "../ast"
import { LocationCalculator } from "./location-calculator"

/**
* Do post-process of parsing an expression.
*
* 1. Set `node.parent`.
* 2. Fix `node.range` and `node.loc` for HTML entities.
*
* @param result The parsing result to modify.
* @param locationCalculator The location calculator to modify.
*/
export function fixLocations(
result: ESLintExtendedProgram,
locationCalculator: LocationCalculator,
): void {
// There are cases which the same node instance appears twice in the tree.
// E.g. `let {a} = {}` // This `a` appears twice at `Property#key` and `Property#value`.
const traversed = new Set<Node | number[] | LocationRange>()

traverseNodes(result.ast, {
visitorKeys: result.visitorKeys,

enterNode(node, parent) {
if (!traversed.has(node)) {
traversed.add(node)
node.parent = parent

// `babel-eslint@8` has shared `Node#range` with multiple nodes.
// See also: https://github.com/vuejs/eslint-plugin-vue/issues/208
if (traversed.has(node.range)) {
if (!traversed.has(node.loc)) {
// However, `Node#loc` may not be shared.
// See also: https://github.com/vuejs/vue-eslint-parser/issues/84
node.loc.start = locationCalculator.getLocFromIndex(
node.range[0],
)
node.loc.end = locationCalculator.getLocFromIndex(
node.range[1],
)
traversed.add(node.loc)
}
} else {
locationCalculator.fixLocation(node)
traversed.add(node.range)
traversed.add(node.loc)
}
}
},

leaveNode() {
// Do nothing.
},
})

for (const token of result.ast.tokens || []) {
locationCalculator.fixLocation(token)
}
for (const comment of result.ast.comments || []) {
locationCalculator.fixLocation(comment)
}
}
9 changes: 9 additions & 0 deletions src/common/parser-options.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as path from "path"

export interface ParserOptions {
// vue-eslint-parser options
parser?: boolean | string
@@ -33,3 +35,10 @@ export interface ParserOptions {
// others
// [key: string]: any
}

export function isSFCFile(parserOptions: ParserOptions) {
if (parserOptions.filePath === "<input>") {
return true
}
return path.extname(parserOptions.filePath || "unknown.vue") === ".vue"
}
6 changes: 2 additions & 4 deletions src/html/parser.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@
* @copyright 2017 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
import * as path from "path"
import assert from "assert"
import last from "lodash/last"
import findLastIndex from "lodash/findLastIndex"
@@ -47,7 +46,7 @@ import {
Text,
} from "./intermediate-tokenizer"
import { Tokenizer } from "./tokenizer"
import { ParserOptions } from "../common/parser-options"
import { isSFCFile, ParserOptions } from "../common/parser-options"

const DIRECTIVE_NAME = /^(?:v-|[.:@#]).*[^.:@#]$/u
const DT_DD = /^d[dt]$/u
@@ -232,8 +231,7 @@ export class Parser {
tokenizer.lineTerminators,
)
this.parserOptions = parserOptions
this.isSFC =
path.extname(parserOptions.filePath || "unknown.vue") === ".vue"
this.isSFC = isSFCFile(parserOptions)
this.document = {
type: "VDocumentFragment",
range: [0, 0],
12 changes: 8 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -82,7 +82,7 @@ export function parseForESLint(
options = Object.assign(
{
comment: true,
ecmaVersion: 2015,
ecmaVersion: 2017,
loc: true,
range: true,
tokens: true,
@@ -92,14 +92,16 @@ export function parseForESLint(

let result: AST.ESLintExtendedProgram
let document: AST.VDocumentFragment | null
let locationCalculator: LocationCalculator | null
if (!isVueFile(code, options)) {
result = parseScript(code, options)
document = null
locationCalculator = null
} else {
const skipParsingScript = options.parser === false
const tokenizer = new HTMLTokenizer(code, options)
const rootAST = new HTMLParser(tokenizer, options).parse()
const locationCalcurator = new LocationCalculator(
locationCalculator = new LocationCalculator(
tokenizer.gaps,
tokenizer.lineTerminators,
)
@@ -119,7 +121,7 @@ export function parseForESLint(
if (skipParsingScript || script == null) {
result = parseScript("", options)
} else {
result = parseScriptElement(script, locationCalcurator, options)
result = parseScriptElement(script, locationCalculator, options)
}

result.ast.templateBody = templateBody
@@ -128,7 +130,9 @@ export function parseForESLint(

result.services = Object.assign(
result.services || {},
services.define(result.ast, document),
services.define(code, result.ast, document, locationCalculator, {
parserOptions: options,
}),
)

return result
Loading