Skip to content

Commit

Permalink
Add support for v-bind same-name shorthand
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed Dec 31, 2023
1 parent aa2de99 commit 3238cb6
Show file tree
Hide file tree
Showing 42 changed files with 10,857 additions and 10 deletions.
10 changes: 1 addition & 9 deletions src/script-setup/scope-analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
isScriptElement,
isScriptSetupElement,
} from "../common/ast-utils"
import { camelize } from "../utils/utils"

const BUILTIN_COMPONENTS = new Set([
"template",
Expand Down Expand Up @@ -94,15 +95,6 @@ const COMPILER_MACROS_AT_ROOT = new Set([
"defineModel",
])

/**
* `casing.camelCase()` converts the beginning to lowercase,
* but does not convert the case of the beginning character when converting with Vue3.
* @see https://github.com/vuejs/vue-next/blob/48de8a42b7fed7a03f7f1ff5d53d6a704252cafe/packages/shared/src/index.ts#L109
*/
function camelize(str: string) {
return str.replace(/-(\w)/gu, (_, c) => (c ? c.toUpperCase() : ""))
}

function capitalize(str: string) {
return str[0].toUpperCase() + str.slice(1)
}
Expand Down
71 changes: 71 additions & 0 deletions src/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { ParserOptions } from "../common/parser-options"
import { isSFCFile } from "../common/parser-options"
import type {
ESLintExpression,
ESLintExtendedProgram,
ESLintIdentifier,
Reference,
Token,
VAttribute,
Expand Down Expand Up @@ -34,6 +36,7 @@ import {
parseVOnExpression,
parseSlotScopeExpression,
parseGenericExpression,
parseScriptFragment,
} from "../script"
import {
createSimpleToken,
Expand All @@ -46,6 +49,7 @@ import {
isTSLang,
} from "../common/ast-utils"
import { insertError } from "../common/error-utils"
import { camelize } from "../utils/utils"

const shorthandSign = /^[.:@#]/u
const shorthandNameMap = { ":": "bind", ".": "bind", "@": "on", "#": "slot" }
Expand Down Expand Up @@ -626,6 +630,14 @@ export function convertToDirective(
}

if (node.value == null) {
if (directive.key.name.name === "bind") {
// v-bind same-name shorthand (Vue 3.4+)
convertForVBindSameNameShorthandValue(
directive,
parserOptions,
locationCalculator,
)
}
return
}

Expand Down Expand Up @@ -677,6 +689,65 @@ export function convertToDirective(
}
}

function convertForVBindSameNameShorthandValue(
directive: VDirective,
parserOptions: ParserOptions,
locationCalculator: LocationCalculatorForHtml,
) {
if (
directive.key.name.name !== "bind" ||
directive.key.argument == null ||
directive.key.argument.type !== "VIdentifier"
) {
return
}
// v-bind same-name shorthand (Vue 3.4+)
const vId = directive.key.argument
const camelName = camelize(vId.name)
let result: ESLintExtendedProgram | null = null
try {
result = parseScriptFragment(
camelName,
locationCalculator.getSubCalculatorAfter(vId.range[0]),
parserOptions,
)
} catch (err) {
debug("[template] Parse error: %s", err)
}
if (
result == null ||
result.ast.body.length !== 1 ||
result.ast.body[0].type !== "ExpressionStatement" ||
result.ast.body[0].expression.type !== "Identifier"
) {
return
}
const id: ESLintIdentifier = result.ast.body[0].expression
id.range[1] = vId.range[1]
id.loc.end = { ...vId.loc.end }
if (id.end != null) {
id.end = vId.end
}
directive.value = {
type: "VExpressionContainer",
range: [...vId.range],
loc: {
start: { ...vId.loc.start },
end: { ...vId.loc.end },
},
parent: directive,
expression: id,
references: [
{
id,
mode: "r",
variable: null,
},
],
}
id.parent = directive.value
}

/**
* Parse the content of the given mustache.
* @param parserOptions The parser options to parse expressions.
Expand Down
6 changes: 6 additions & 0 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @see https://github.com/vuejs/vue-next/blob/48de8a42b7fed7a03f7f1ff5d53d6a704252cafe/packages/shared/src/index.ts#L109
*/
export function camelize(str: string) {
return str.replace(/-(\w)/gu, (_, c) => (c ? c.toUpperCase() : ""))
}
62 changes: 61 additions & 1 deletion test/fixtures/ast/directive-shorthands/ast.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,67 @@
}
]
},
"value": null
"value": {
"type": "VExpressionContainer",
"range": [
21,
22
],
"loc": {
"start": {
"column": 10,
"line": 2
},
"end": {
"column": 11,
"line": 2
}
},
"expression": {
"type": "Identifier",
"start": 21,
"loc": {
"start": {
"line": 2,
"column": 10
},
"end": {
"column": 11,
"line": 2
}
},
"range": [
21,
22
],
"name": "a"
},
"references": [
{
"id": {
"type": "Identifier",
"start": 21,
"loc": {
"start": {
"line": 2,
"column": 10
},
"end": {
"column": 11,
"line": 2
}
},
"range": [
21,
22
],
"name": "a"
},
"mode": "r",
"variable": null
}
]
}
}
]
},
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/ast/directive-shorthands/tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@
"children": []
}
]
},
{
"type": "VExpressionContainer",
"text": "a",
"children": [
{
"type": "Identifier",
"text": "a",
"children": []
}
]
}
]
}
Expand Down
Loading

0 comments on commit 3238cb6

Please sign in to comment.