first commit

This commit is contained in:
monjack
2025-06-20 18:01:48 +08:00
commit 6daa6d65c1
24611 changed files with 2512443 additions and 0 deletions

View File

@ -0,0 +1,203 @@
// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------
/**
* Capitalize a string.
* @param {string} str
*/
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
/**
* Checks whether the given string has symbols.
* @param {string} str
*/
function hasSymbols(str) {
return /[!"#%&'()*+,./:;<=>?@[\\\]^`{|}]/u.exec(str) // without " ", "$", "-" and "_"
}
/**
* Checks whether the given string has upper.
* @param {string} str
*/
function hasUpper(str) {
return /[A-Z]/u.exec(str)
}
/**
* Convert text to kebab-case
* @param {string} str Text to be converted
* @return {string}
*/
function kebabCase(str) {
return str
.replace(/_/gu, '-')
.replace(/\B([A-Z])/gu, '-$1')
.toLowerCase()
}
/**
* Checks whether the given string is kebab-case.
* @param {string} str
*/
function isKebabCase(str) {
if (
hasUpper(str) ||
hasSymbols(str) ||
/^-/u.exec(str) || // starts with hyphen is not kebab-case
/_|--|\s/u.exec(str)
) {
return false
}
return true
}
/**
* Convert text to snake_case
* @param {string} str Text to be converted
* @return {string}
*/
function snakeCase(str) {
return str
.replace(/\B([A-Z])/gu, '_$1')
.replace(/-/gu, '_')
.toLowerCase()
}
/**
* Checks whether the given string is snake_case.
* @param {string} str
*/
function isSnakeCase(str) {
if (hasUpper(str) || hasSymbols(str) || /-|__|\s/u.exec(str)) {
return false
}
return true
}
/**
* Convert text to camelCase
* @param {string} str Text to be converted
* @return {string} Converted string
*/
function camelCase(str) {
if (isPascalCase(str)) {
return str.charAt(0).toLowerCase() + str.slice(1)
}
return str.replace(/[-_](\w)/gu, (_, c) => (c ? c.toUpperCase() : ''))
}
/**
* Checks whether the given string is camelCase.
* @param {string} str
*/
function isCamelCase(str) {
if (
hasSymbols(str) ||
/^[A-Z]/u.exec(str) ||
/-|_|\s/u.exec(str) // kebab or snake or space
) {
return false
}
return true
}
/**
* Convert text to PascalCase
* @param {string} str Text to be converted
* @return {string} Converted string
*/
function pascalCase(str) {
return capitalize(camelCase(str))
}
/**
* Checks whether the given string is PascalCase.
* @param {string} str
*/
function isPascalCase(str) {
if (
hasSymbols(str) ||
/^[a-z]/u.exec(str) ||
/-|_|\s/u.exec(str) // kebab or snake or space
) {
return false
}
return true
}
const convertersMap = {
'kebab-case': kebabCase,
snake_case: snakeCase,
camelCase,
PascalCase: pascalCase
}
const checkersMap = {
'kebab-case': isKebabCase,
snake_case: isSnakeCase,
camelCase: isCamelCase,
PascalCase: isPascalCase
}
/**
* Return case checker
* @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of checker to return ('camelCase', 'kebab-case', 'PascalCase')
* @return {isKebabCase|isCamelCase|isPascalCase|isSnakeCase}
*/
function getChecker(name) {
return checkersMap[name] || isPascalCase
}
/**
* Return case converter
* @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of converter to return ('camelCase', 'kebab-case', 'PascalCase')
* @return {kebabCase|camelCase|pascalCase|snakeCase}
*/
function getConverter(name) {
return convertersMap[name] || pascalCase
}
module.exports = {
allowedCaseOptions: ['camelCase', 'kebab-case', 'PascalCase'],
/**
* Return case converter
* @param {string} name type of converter to return ('camelCase', 'kebab-case', 'PascalCase')
* @return {kebabCase|camelCase|pascalCase|snakeCase}
*/
getConverter,
/**
* Return case checker
* @param {string} name type of checker to return ('camelCase', 'kebab-case', 'PascalCase')
* @return {isKebabCase|isCamelCase|isPascalCase|isSnakeCase}
*/
getChecker,
/**
* Return case exact converter.
* If the converted result is not the correct case, the original value is returned.
* @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of converter to return ('camelCase', 'kebab-case', 'PascalCase')
* @return {kebabCase|camelCase|pascalCase|snakeCase}
*/
getExactConverter(name) {
const converter = getConverter(name)
const checker = getChecker(name)
return (str) => {
const result = converter(str)
return checker(result) ? result : str /* cannot convert */
}
},
camelCase,
pascalCase,
kebabCase,
snakeCase,
isCamelCase,
isPascalCase,
isKebabCase,
isSnakeCase,
capitalize
}

View File

@ -0,0 +1 @@
["acronym","applet","basefont","bgsound","big","blink","center","command","content","dir","element","font","frame","frameset","image","isindex","keygen","listing","marquee","menuitem","multicol","nextid","nobr","noembed","noframes","plaintext","shadow","spacer","strike","tt","xmp"]

View File

@ -0,0 +1,259 @@
/**
* @typedef { { exceptions?: string[] } } CommentParserConfig
* @typedef { (comment: ParsedHTMLComment) => void } HTMLCommentVisitor
* @typedef { { includeDirectives?: boolean } } CommentVisitorOption
*
* @typedef { Token & { type: 'HTMLCommentOpen' } } HTMLCommentOpen
* @typedef { Token & { type: 'HTMLCommentOpenDecoration' } } HTMLCommentOpenDecoration
* @typedef { Token & { type: 'HTMLCommentValue' } } HTMLCommentValue
* @typedef { Token & { type: 'HTMLCommentClose' } } HTMLCommentClose
* @typedef { Token & { type: 'HTMLCommentCloseDecoration' } } HTMLCommentCloseDecoration
* @typedef { { open: HTMLCommentOpen, openDecoration: HTMLCommentOpenDecoration | null, value: HTMLCommentValue | null, closeDecoration: HTMLCommentCloseDecoration | null, close: HTMLCommentClose } } ParsedHTMLComment
*/
// -----------------------------------------------------------------------------
// Requirements
// -----------------------------------------------------------------------------
const utils = require('./')
// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------
const COMMENT_DIRECTIVE = /^\s*eslint-(?:en|dis)able/
const IE_CONDITIONAL_IF = /^\[if\s+/
const IE_CONDITIONAL_ENDIF = /\[endif\]$/
/** @type { 'HTMLCommentOpen' } */
const TYPE_HTML_COMMENT_OPEN = 'HTMLCommentOpen'
/** @type { 'HTMLCommentOpenDecoration' } */
const TYPE_HTML_COMMENT_OPEN_DECORATION = 'HTMLCommentOpenDecoration'
/** @type { 'HTMLCommentValue' } */
const TYPE_HTML_COMMENT_VALUE = 'HTMLCommentValue'
/** @type { 'HTMLCommentClose' } */
const TYPE_HTML_COMMENT_CLOSE = 'HTMLCommentClose'
/** @type { 'HTMLCommentCloseDecoration' } */
const TYPE_HTML_COMMENT_CLOSE_DECORATION = 'HTMLCommentCloseDecoration'
/**
* @param {HTMLComment} comment
* @returns {boolean}
*/
function isCommentDirective(comment) {
return COMMENT_DIRECTIVE.test(comment.value)
}
/**
* @param {HTMLComment} comment
* @returns {boolean}
*/
function isIEConditionalComment(comment) {
return (
IE_CONDITIONAL_IF.test(comment.value) ||
IE_CONDITIONAL_ENDIF.test(comment.value)
)
}
/**
* Define HTML comment parser
*
* @param {SourceCode} sourceCode The source code instance.
* @param {CommentParserConfig | null} config The config.
* @returns { (node: Token) => (ParsedHTMLComment | null) } HTML comment parser.
*/
function defineParser(sourceCode, config) {
config = config || {}
const exceptions = config.exceptions || []
/**
* Get a open decoration string from comment contents.
* @param {string} contents comment contents
* @returns {string} decoration string
*/
function getOpenDecoration(contents) {
let decoration = ''
for (const exception of exceptions) {
const length = exception.length
let index = 0
while (contents.startsWith(exception, index)) {
index += length
}
const exceptionLength = index
if (decoration.length < exceptionLength) {
decoration = contents.slice(0, exceptionLength)
}
}
return decoration
}
/**
* Get a close decoration string from comment contents.
* @param {string} contents comment contents
* @returns {string} decoration string
*/
function getCloseDecoration(contents) {
let decoration = ''
for (const exception of exceptions) {
const length = exception.length
let index = contents.length
while (contents.endsWith(exception, index)) {
index -= length
}
const exceptionLength = contents.length - index
if (decoration.length < exceptionLength) {
decoration = contents.slice(index)
}
}
return decoration
}
/**
* Parse HTMLComment.
* @param {Token} node a comment token
* @returns {ParsedHTMLComment | null} the result of HTMLComment tokens.
*/
return function parseHTMLComment(node) {
if (node.type !== 'HTMLComment') {
// Is not HTMLComment
return null
}
const htmlCommentText = sourceCode.getText(node)
if (
!htmlCommentText.startsWith('<!--') ||
!htmlCommentText.endsWith('-->')
) {
// Is not normal HTML Comment
// e.g. Error Code: "abrupt-closing-of-empty-comment", "incorrectly-closed-comment"
return null
}
let valueText = htmlCommentText.slice(4, -3)
const openDecorationText = getOpenDecoration(valueText)
valueText = valueText.slice(openDecorationText.length)
const firstCharIndex = valueText.search(/\S/)
const beforeSpace =
firstCharIndex >= 0 ? valueText.slice(0, firstCharIndex) : valueText
valueText = valueText.slice(beforeSpace.length)
const closeDecorationText = getCloseDecoration(valueText)
if (closeDecorationText) {
valueText = valueText.slice(0, -closeDecorationText.length)
}
const lastCharIndex = valueText.search(/\S\s*$/)
const afterSpace =
lastCharIndex >= 0 ? valueText.slice(lastCharIndex + 1) : valueText
if (afterSpace) {
valueText = valueText.slice(0, -afterSpace.length)
}
let tokenIndex = node.range[0]
/**
* @param {string} type
* @param {string} value
* @returns {any}
*/
const createToken = (type, value) => {
/** @type {Range} */
const range = [tokenIndex, tokenIndex + value.length]
tokenIndex = range[1]
/** @type {SourceLocation} */
let loc
return {
type,
value,
range,
get loc() {
if (loc) {
return loc
}
return (loc = {
start: sourceCode.getLocFromIndex(range[0]),
end: sourceCode.getLocFromIndex(range[1])
})
}
}
}
/** @type {HTMLCommentOpen} */
const open = createToken(TYPE_HTML_COMMENT_OPEN, '<!--')
/** @type {HTMLCommentOpenDecoration | null} */
const openDecoration = openDecorationText
? createToken(TYPE_HTML_COMMENT_OPEN_DECORATION, openDecorationText)
: null
tokenIndex += beforeSpace.length
/** @type {HTMLCommentValue | null} */
const value = valueText
? createToken(TYPE_HTML_COMMENT_VALUE, valueText)
: null
tokenIndex += afterSpace.length
/** @type {HTMLCommentCloseDecoration | null} */
const closeDecoration = closeDecorationText
? createToken(TYPE_HTML_COMMENT_CLOSE_DECORATION, closeDecorationText)
: null
/** @type {HTMLCommentClose} */
const close = createToken(TYPE_HTML_COMMENT_CLOSE, '-->')
return {
/** HTML comment open (`<!--`) */
open,
/** decoration of the start of HTML comments. (`*****` when `<!--*****`) */
openDecoration,
/** value of HTML comment. whitespaces and other tokens are not included. */
value,
/** decoration of the end of HTML comments. (`*****` when `*****-->`) */
closeDecoration,
/** HTML comment close (`-->`) */
close
}
}
}
/**
* Define HTML comment visitor
*
* @param {RuleContext} context The rule context.
* @param {CommentParserConfig | null} config The config.
* @param {HTMLCommentVisitor} visitHTMLComment The HTML comment visitor.
* @param {CommentVisitorOption} [visitorOption] The option for visitor.
* @returns {RuleListener} HTML comment visitor.
*/
function defineVisitor(context, config, visitHTMLComment, visitorOption) {
return {
Program(node) {
visitorOption = visitorOption || {}
if (utils.hasInvalidEOF(node)) {
return
}
if (!node.templateBody) {
return
}
const parse = defineParser(context.getSourceCode(), config)
for (const comment of node.templateBody.comments) {
if (comment.type !== 'HTMLComment') {
continue
}
if (!visitorOption.includeDirectives && isCommentDirective(comment)) {
// ignore directives
continue
}
if (isIEConditionalComment(comment)) {
// ignore IE conditional
continue
}
const tokens = parse(comment)
if (tokens) {
visitHTMLComment(tokens)
}
}
}
}
}
module.exports = {
defineVisitor
}

View File

@ -0,0 +1 @@
["html","body","base","head","link","meta","style","title","address","article","aside","footer","header","h1","h2","h3","h4","h5","h6","hgroup","nav","section","div","dd","dl","dt","figcaption","figure","hr","img","li","main","ol","p","pre","ul","a","b","abbr","bdi","bdo","br","cite","code","data","dfn","em","i","kbd","mark","q","rp","rt","rtc","ruby","s","samp","small","span","strong","sub","sup","time","u","var","wbr","area","audio","map","track","video","embed","object","param","source","canvas","script","noscript","del","ins","caption","col","colgroup","table","thead","tbody","tfoot","td","th","tr","button","datalist","fieldset","form","input","label","legend","meter","optgroup","option","output","progress","select","textarea","details","dialog","menu","menuitem","summary","content","element","shadow","template","slot","blockquote","iframe","noframes","picture"]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
'use strict'
/**
* Check whether the given token is a wildcard.
* @param {Token|undefined|null} token The token to check.
* @returns {boolean} `true` if the token is a wildcard.
*/
function isWildcard(token) {
return token != null && token.type === 'Punctuator' && token.value === '*'
}
/**
* Check whether the given token is a question.
* @param {Token|undefined|null} token The token to check.
* @returns {boolean} `true` if the token is a question.
*/
function isQuestion(token) {
return token != null && token.type === 'Punctuator' && token.value === '?'
}
/**
* Check whether the given token is an extends keyword.
* @param {Token|undefined|null} token The token to check.
* @returns {boolean} `true` if the token is an extends keywordn.
*/
function isExtendsKeyword(token) {
return token != null && token.type === 'Keyword' && token.value === 'extends'
}
/**
* Check whether the given token is a whitespace.
* @param {Token|undefined|null} token The token to check.
* @returns {boolean} `true` if the token is a whitespace.
*/
function isNotWhitespace(token) {
return (
token != null &&
token.type !== 'HTMLWhitespace' &&
(token.type !== 'JSXText' || !!token.value.trim())
)
}
/**
* Check whether the given token is a comment.
* @param {Token|undefined|null} token The token to check.
* @returns {boolean} `true` if the token is a comment.
*/
function isComment(token) {
return (
token != null &&
(token.type === 'Block' ||
token.type === 'Line' ||
token.type === 'Shebang' ||
(typeof token.type ===
'string' /* Although acorn supports new tokens, espree may not yet support new tokens.*/ &&
token.type.endsWith('Comment')))
)
}
/**
* Check whether the given token is a comment.
* @param {Token|undefined|null} token The token to check.
* @returns {boolean} `false` if the token is a comment.
*/
function isNotComment(token) {
return (
token != null &&
token.type !== 'Block' &&
token.type !== 'Line' &&
token.type !== 'Shebang' &&
!(
typeof token.type ===
'string' /* Although acorn supports new tokens, espree may not yet support new tokens.*/ &&
token.type.endsWith('Comment')
)
)
}
/**
* Check whether the given node is not an empty text node.
* @param {ASTNode} node The node to check.
* @returns {boolean} `false` if the token is empty text node.
*/
function isNotEmptyTextNode(node) {
return !(node.type === 'VText' && node.value.trim() === '')
}
/**
* Check whether the given token is a pipe operator.
* @param {Token|undefined|null} token The token to check.
* @returns {boolean} `true` if the token is a pipe operator.
*/
function isPipeOperator(token) {
return token != null && token.type === 'Punctuator' && token.value === '|'
}
/**
* Get the last element.
* @template T
* @param {T[]} xs The array to get the last element.
* @returns {T | undefined} The last element or undefined.
*/
function last(xs) {
return xs.length === 0 ? undefined : xs[xs.length - 1]
}
module.exports = {
isWildcard,
isQuestion,
isExtendsKeyword,
isNotWhitespace,
isComment,
isNotComment,
isNotEmptyTextNode,
isPipeOperator,
last
}

3136
app_vue/node_modules/eslint-plugin-vue/lib/utils/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
[
"a",
"abbr",
"audio",
"b",
"bdi",
"bdo",
"canvas",
"cite",
"code",
"data",
"del",
"dfn",
"em",
"i",
"iframe",
"ins",
"kbd",
"label",
"map",
"mark",
"noscript",
"object",
"output",
"picture",
"q",
"ruby",
"s",
"samp",
"small",
"span",
"strong",
"sub",
"sup",
"svg",
"time",
"u",
"var",
"video"
]

View File

@ -0,0 +1,18 @@
[
"abstract", "arguments", "await", "boolean",
"break", "byte", "case", "catch",
"char", "class", "const", "continue",
"debugger", "default", "delete", "do",
"double", "else", "enum", "eval",
"export", "extends", "false", "final",
"finally", "float", "for", "function",
"goto", "if", "implements", "import",
"in", "instanceof", "int", "interface",
"let", "long", "native", "new",
"null", "package", "private", "protected",
"public", "return", "short", "static",
"super", "switch", "synchronized", "this",
"throw", "throws", "transient", "true",
"try", "typeof", "var", "void",
"volatile", "while", "with", "yield"
]

View File

@ -0,0 +1,68 @@
[
"unidentified", "alt", "alt-graph", "caps-lock", "control", "fn", "fn-lock",
"meta", "num-lock", "scroll-lock", "shift", "symbol", "symbol-lock", "hyper",
"super", "enter", "tab", "arrow-down", "arrow-left", "arrow-right",
"arrow-up", "end", "home", "page-down", "page-up", "backspace", "clear",
"copy", "cr-sel", "cut", "delete", "erase-eof", "ex-sel", "insert", "paste",
"redo", "undo", "accept", "again", "attn", "cancel", "context-menu", "escape",
"execute", "find", "help", "pause", "select", "zoom-in", "zoom-out",
"brightness-down", "brightness-up", "eject", "log-off", "power",
"print-screen", "hibernate", "standby", "wake-up", "all-candidates",
"alphanumeric", "code-input", "compose", "convert", "dead", "final-mode",
"group-first", "group-last", "group-next", "group-previous", "mode-change",
"next-candidate", "non-convert", "previous-candidate", "process",
"single-candidate", "hangul-mode", "hanja-mode", "junja-mode", "eisu",
"hankaku", "hiragana", "hiragana-katakana", "kana-mode", "kanji-mode",
"katakana", "romaji", "zenkaku", "zenkaku-hankaku", "f1", "f2", "f3", "f4",
"f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "soft1", "soft2", "soft3",
"soft4", "channel-down", "channel-up", "close", "mail-forward", "mail-reply",
"mail-send", "media-close", "media-fast-forward", "media-pause",
"media-play-pause", "media-record", "media-rewind", "media-stop",
"media-track-next", "media-track-previous", "new", "open", "print", "save",
"spell-check", "key11", "key12", "audio-balance-left", "audio-balance-right",
"audio-bass-boost-down", "audio-bass-boost-toggle", "audio-bass-boost-up",
"audio-fader-front", "audio-fader-rear", "audio-surround-mode-next",
"audio-treble-down", "audio-treble-up", "audio-volume-down",
"audio-volume-up", "audio-volume-mute", "microphone-toggle",
"microphone-volume-down", "microphone-volume-up", "microphone-volume-mute",
"speech-correction-list", "speech-input-toggle", "launch-application1",
"launch-application2", "launch-calendar", "launch-contacts", "launch-mail",
"launch-media-player", "launch-music-player", "launch-phone",
"launch-screen-saver", "launch-spreadsheet", "launch-web-browser",
"launch-web-cam", "launch-word-processor", "browser-back",
"browser-favorites", "browser-forward", "browser-home", "browser-refresh",
"browser-search", "browser-stop", "app-switch", "call", "camera",
"camera-focus", "end-call", "go-back", "go-home", "headset-hook",
"last-number-redial", "notification", "manner-mode", "voice-dial", "t-v",
"t-v3-d-mode", "t-v-antenna-cable", "t-v-audio-description",
"t-v-audio-description-mix-down", "t-v-audio-description-mix-up",
"t-v-contents-menu", "t-v-data-service", "t-v-input", "t-v-input-component1",
"t-v-input-component2", "t-v-input-composite1", "t-v-input-composite2",
"t-v-input-h-d-m-i1", "t-v-input-h-d-m-i2", "t-v-input-h-d-m-i3",
"t-v-input-h-d-m-i4", "t-v-input-v-g-a1", "t-v-media-context", "t-v-network",
"t-v-number-entry", "t-v-power", "t-v-radio-service", "t-v-satellite",
"t-v-satellite-b-s", "t-v-satellite-c-s", "t-v-satellite-toggle",
"t-v-terrestrial-analog", "t-v-terrestrial-digital", "t-v-timer",
"a-v-r-input", "a-v-r-power", "color-f0-red", "color-f1-green",
"color-f2-yellow", "color-f3-blue", "color-f4-grey", "color-f5-brown",
"closed-caption-toggle", "dimmer", "display-swap", "d-v-r", "exit",
"favorite-clear0", "favorite-clear1", "favorite-clear2", "favorite-clear3",
"favorite-recall0", "favorite-recall1", "favorite-recall2",
"favorite-recall3", "favorite-store0", "favorite-store1", "favorite-store2",
"favorite-store3", "guide", "guide-next-day", "guide-previous-day", "info",
"instant-replay", "link", "list-program", "live-content", "lock",
"media-apps", "media-last", "media-skip-backward", "media-skip-forward",
"media-step-backward", "media-step-forward", "media-top-menu", "navigate-in",
"navigate-next", "navigate-out", "navigate-previous", "next-favorite-channel",
"next-user-profile", "on-demand", "pairing", "pin-p-down", "pin-p-move",
"pin-p-toggle", "pin-p-up", "play-speed-down", "play-speed-reset",
"play-speed-up", "random-toggle", "rc-low-battery", "record-speed-next",
"rf-bypass", "scan-channels-toggle", "screen-mode-next", "settings",
"split-screen-toggle", "s-t-b-input", "s-t-b-power", "subtitle", "teletext",
"video-mode-next", "wink", "zoom-toggle", "audio-volume-down",
"audio-volume-up", "audio-volume-mute", "browser-back", "browser-forward",
"channel-down", "channel-up", "context-menu", "eject", "end", "enter", "home",
"media-fast-forward", "media-play", "media-play-pause", "media-record",
"media-rewind", "media-stop", "media-next-track", "media-pause",
"media-previous-track", "power", "unidentified"
]

View File

@ -0,0 +1,99 @@
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
/** @type { { [key: number]: string } } */
module.exports = {
8: 'backspace',
9: 'tab',
13: 'enter',
16: 'shift',
17: 'ctrl',
18: 'alt',
19: 'pause', // windows
20: 'caps-lock',
27: 'escape',
32: 'space', // Vue.js specially key name.
33: 'page-up',
34: 'page-down',
35: 'end',
36: 'home',
37: 'arrow-left',
38: 'arrow-up',
39: 'arrow-right',
40: 'arrow-down',
45: 'insert', // windows
46: 'delete',
// If mistakenly use it in Vue.js 2.x, it will be irreversibly broken. Therefore, it will not be autofix.
// '48': '0',
// '49': '1',
// '50': '2',
// '51': '3',
// '52': '4',
// '53': '5',
// '54': '6',
// '55': '7',
// '56': '8',
// '57': '9',
65: 'a',
66: 'b',
67: 'c',
68: 'd',
69: 'e',
70: 'f',
71: 'g',
72: 'h',
73: 'i',
74: 'j',
75: 'k',
76: 'l',
77: 'm',
78: 'n',
79: 'o',
80: 'p',
81: 'q',
82: 'r',
83: 's',
84: 't',
85: 'u',
86: 'v',
87: 'w',
88: 'x',
89: 'y',
90: 'z',
// The key value may change depending on the OS.
// '91': 'meta' ,// Win: 'os'?
// '92': 'meta', // Win: 'meta' Mac: ?
// '93': 'meta', // Win: 'context-menu' Mac: 'meta'
// Cannot determine numpad with key.
// '96': 'numpad-0',
// '97': 'numpad-1',
// '98': 'numpad-2',
// '99': 'numpad-3',
// '100': 'numpad-4',
// '101': 'numpad-5',
// '102': 'numpad-6',
// '103': 'numpad-7',
// '104': 'numpad-8',
// '105': 'numpad-9',
// '106': 'multiply',
// '107': 'add',
// '109': 'subtract',
// '110': 'decimal',
// '111': 'divide',
112: 'f1',
113: 'f2',
114: 'f3',
115: 'f4',
116: 'f5',
117: 'f6',
118: 'f7',
119: 'f8',
120: 'f9',
121: 'f10',
122: 'f11',
123: 'f12',
144: 'num-lock',
145: 'scroll-lock'
}

View File

@ -0,0 +1,697 @@
/**
* @author Yosuke Ota
* @copyright 2021 Yosuke Ota. All rights reserved.
* See LICENSE file in root directory for full license.
*/
'use strict'
const utils = require('./index')
const eslintUtils = require('eslint-utils')
/**
* @typedef {import('./style-variables').StyleVariablesContext} StyleVariablesContext
*/
/**
* @typedef {object} IHasPropertyOption
* @property {boolean} [unknownCallAsAny]
*/
/**
* @typedef {object} IPropertyReferences
* @property { (name: string, option?: IHasPropertyOption) => boolean } hasProperty
* @property { () => Map<string, {nodes:ASTNode[]}> } allProperties
* @property { (name: string) => IPropertyReferences } getNest
*/
// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------
/** @type {IPropertyReferences} */
const ANY = {
hasProperty: () => true,
allProperties: () => new Map(),
getNest: () => ANY
}
/** @type {IPropertyReferences} */
const NEVER = {
hasProperty: () => false,
allProperties: () => new Map(),
getNest: () => NEVER
}
/**
* @param {RuleContext} context
* @param {Identifier} id
* @returns {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration | null}
*/
function findFunction(context, id) {
const calleeVariable = utils.findVariableByIdentifier(context, id)
if (!calleeVariable) {
return null
}
if (calleeVariable.defs.length === 1) {
const def = calleeVariable.defs[0]
if (def.node.type === 'FunctionDeclaration') {
return def.node
}
if (
def.type === 'Variable' &&
def.parent.kind === 'const' &&
def.node.init
) {
if (
def.node.init.type === 'FunctionExpression' ||
def.node.init.type === 'ArrowFunctionExpression'
) {
return def.node.init
}
if (def.node.init.type === 'Identifier') {
return findFunction(context, def.node.init)
}
}
}
return null
}
// ------------------------------------------------------------------------------
// Public
// ------------------------------------------------------------------------------
module.exports = {
definePropertyReferenceExtractor,
mergePropertyReferences
}
/**
* @param {RuleContext} context The rule context.
*/
function definePropertyReferenceExtractor(context) {
/** @type {Map<Expression, IPropertyReferences>} */
const cacheForExpression = new Map()
/** @type {Map<Pattern, IPropertyReferences>} */
const cacheForPattern = new Map()
/** @type {Map<FunctionExpression | ArrowFunctionExpression | FunctionDeclaration, Map<number, IPropertyReferences>>} */
const cacheForFunction = new Map()
/** @type {{ toRefNodes: Set<ESNode>, toRefsNodes: Set<ESNode>} | null} */
let toRefSet = null
let isFunctionalTemplate = false
const templateBody = context.getSourceCode().ast.templateBody
if (templateBody) {
isFunctionalTemplate = utils.hasAttribute(templateBody, 'functional')
}
function getToRefSet() {
if (toRefSet) {
return toRefSet
}
const tracker = new eslintUtils.ReferenceTracker(
context.getSourceCode().scopeManager.scopes[0]
)
const toRefNodes = new Set()
for (const { node } of tracker.iterateEsmReferences(
utils.createCompositionApiTraceMap({
[eslintUtils.ReferenceTracker.ESM]: true,
toRef: {
[eslintUtils.ReferenceTracker.CALL]: true
}
})
)) {
toRefNodes.add(node)
}
const toRefsNodes = new Set()
for (const { node } of tracker.iterateEsmReferences(
utils.createCompositionApiTraceMap({
[eslintUtils.ReferenceTracker.ESM]: true,
toRefs: {
[eslintUtils.ReferenceTracker.CALL]: true
}
})
)) {
toRefsNodes.add(node)
}
return (toRefSet = { toRefNodes, toRefsNodes })
}
/**
* Collects the property references for member expr.
* @implements IPropertyReferences
*/
class PropertyReferencesForMember {
/**
*
* @param {MemberExpression} node
* @param {string} name
* @param {boolean} withInTemplate
*/
constructor(node, name, withInTemplate) {
this.node = node
this.name = name
this.withInTemplate = withInTemplate
}
/**
* @param {string} name
*/
hasProperty(name) {
return name === this.name
}
allProperties() {
return new Map([[this.name, { nodes: [this.node.property] }]])
}
/**
* @param {string} name
* @returns {IPropertyReferences}
*/
getNest(name) {
return name === this.name
? extractFromExpression(this.node, this.withInTemplate)
: NEVER
}
}
/**
* Collects the property references for object.
* @implements IPropertyReferences
*/
class PropertyReferencesForObject {
constructor() {
/** @type {Record<string, AssignmentProperty[]>} */
this.properties = Object.create(null)
}
/**
* @param {string} name
*/
hasProperty(name) {
return Boolean(this.properties[name])
}
allProperties() {
const result = new Map()
for (const [name, nodes] of Object.entries(this.properties)) {
result.set(name, { nodes: nodes.map((node) => node.key) })
}
return result
}
/**
* @param {string} name
* @returns {IPropertyReferences}
*/
getNest(name) {
const properties = this.properties[name]
return properties
? mergePropertyReferences(
properties.map((property) => getNestFromPattern(property.value))
)
: NEVER
/**
* @param {Pattern} pattern
* @returns {IPropertyReferences}
*/
function getNestFromPattern(pattern) {
if (pattern.type === 'ObjectPattern') {
return extractFromObjectPattern(pattern)
}
if (pattern.type === 'Identifier') {
return extractFromIdentifier(pattern)
} else if (pattern.type === 'AssignmentPattern') {
return getNestFromPattern(pattern.left)
}
return ANY
}
}
}
/**
* Extract the property references from Expression.
* @param {Identifier | MemberExpression | ChainExpression | ThisExpression | CallExpression} node
* @param {boolean} withInTemplate
* @returns {IPropertyReferences}
*/
function extractFromExpression(node, withInTemplate) {
const ref = cacheForExpression.get(node)
if (ref) {
return ref
}
cacheForExpression.set(node, ANY)
const result = extractWithoutCache()
cacheForExpression.set(node, result)
return result
function extractWithoutCache() {
const parent = node.parent
if (parent.type === 'AssignmentExpression') {
if (withInTemplate) {
return NEVER
}
if (parent.right === node) {
// `({foo} = arg)`
return extractFromPattern(parent.left)
}
return NEVER
} else if (parent.type === 'VariableDeclarator') {
if (withInTemplate) {
return NEVER
}
if (parent.init === node) {
// `const {foo} = arg`
// `const foo = arg`
return extractFromPattern(parent.id)
}
return NEVER
} else if (parent.type === 'MemberExpression') {
if (parent.object === node) {
// `arg.foo`
const name = utils.getStaticPropertyName(parent)
if (name) {
return new PropertyReferencesForMember(parent, name, withInTemplate)
} else {
return ANY
}
}
return NEVER
} else if (parent.type === 'CallExpression') {
if (withInTemplate) {
return NEVER
}
const argIndex = parent.arguments.indexOf(node)
if (argIndex > -1) {
// `foo(arg)`
return extractFromCall(parent, argIndex)
}
} else if (parent.type === 'ChainExpression') {
return extractFromExpression(parent, withInTemplate)
} else if (
parent.type === 'ArrowFunctionExpression' ||
parent.type === 'ReturnStatement' ||
parent.type === 'VExpressionContainer' ||
parent.type === 'Property' ||
parent.type === 'ArrayExpression'
) {
// Maybe used externally.
if (maybeExternalUsed(parent)) {
return ANY
}
}
return NEVER
}
/**
* @param {ASTNode} parentTarget
* @returns {boolean}
*/
function maybeExternalUsed(parentTarget) {
if (
parentTarget.type === 'ReturnStatement' ||
parentTarget.type === 'VExpressionContainer'
) {
return true
}
if (parentTarget.type === 'ArrayExpression') {
return maybeExternalUsed(parentTarget.parent)
}
if (parentTarget.type === 'Property') {
return maybeExternalUsed(parentTarget.parent.parent)
}
if (parentTarget.type === 'ArrowFunctionExpression') {
return parentTarget.body === node
}
return false
}
}
/**
* Extract the property references from one parameter of the function.
* @param {Pattern} node
* @returns {IPropertyReferences}
*/
function extractFromPattern(node) {
const ref = cacheForPattern.get(node)
if (ref) {
return ref
}
cacheForPattern.set(node, ANY)
const result = extractWithoutCache()
cacheForPattern.set(node, result)
return result
function extractWithoutCache() {
while (node.type === 'AssignmentPattern') {
node = node.left
}
if (node.type === 'RestElement' || node.type === 'ArrayPattern') {
// cannot check
return NEVER
}
if (node.type === 'ObjectPattern') {
return extractFromObjectPattern(node)
}
if (node.type === 'Identifier') {
return extractFromIdentifier(node)
}
return NEVER
}
}
/**
* Extract the property references from ObjectPattern.
* @param {ObjectPattern} node
* @returns {IPropertyReferences}
*/
function extractFromObjectPattern(node) {
const refs = new PropertyReferencesForObject()
for (const prop of node.properties) {
if (prop.type === 'Property') {
const name = utils.getStaticPropertyName(prop)
if (name) {
const list = refs.properties[name] || (refs.properties[name] = [])
list.push(prop)
} else {
// If cannot trace name, everything is used!
return ANY
}
} else {
// If use RestElement, everything is used!
return ANY
}
}
return refs
}
/**
* Extract the property references from id.
* @param {Identifier} node
* @returns {IPropertyReferences}
*/
function extractFromIdentifier(node) {
const variable = utils.findVariableByIdentifier(context, node)
if (!variable) {
return NEVER
}
return mergePropertyReferences(
variable.references.map((reference) => {
const id = reference.identifier
return extractFromExpression(id, false)
})
)
}
/**
* Extract the property references from call.
* @param {CallExpression} node
* @param {number} argIndex
* @returns {IPropertyReferences}
*/
function extractFromCall(node, argIndex) {
if (node.callee.type !== 'Identifier') {
return {
hasProperty(_name, options) {
return Boolean(options && options.unknownCallAsAny)
},
allProperties: () => new Map(),
getNest: () => ANY
}
}
const fnNode = findFunction(context, node.callee)
if (!fnNode) {
if (argIndex === 0) {
if (getToRefSet().toRefNodes.has(node)) {
return extractFromToRef(node)
} else if (getToRefSet().toRefsNodes.has(node)) {
return extractFromToRefs(node)
}
}
return {
hasProperty(_name, options) {
return Boolean(options && options.unknownCallAsAny)
},
allProperties: () => new Map(),
getNest: () => ANY
}
}
return extractFromFunctionParam(fnNode, argIndex)
}
/**
* Extract the property references from function param.
* @param {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration} node
* @param {number} argIndex
* @returns {IPropertyReferences}
*/
function extractFromFunctionParam(node, argIndex) {
let cacheForIndexes = cacheForFunction.get(node)
if (!cacheForIndexes) {
cacheForIndexes = new Map()
cacheForFunction.set(node, cacheForIndexes)
}
const ref = cacheForIndexes.get(argIndex)
if (ref) {
return ref
}
cacheForIndexes.set(argIndex, NEVER)
const arg = node.params[argIndex]
if (!arg) {
return NEVER
}
const result = extractFromPattern(arg)
cacheForIndexes.set(argIndex, result)
return result
}
/**
* Extract the property references from path.
* @param {string} pathString
* @param {Identifier | Literal | TemplateLiteral} node
* @returns {IPropertyReferences}
*/
function extractFromPath(pathString, node) {
return extractFromSegments(pathString.split('.'))
/**
* @param {string[]} segments
* @returns {IPropertyReferences}
*/
function extractFromSegments(segments) {
if (!segments.length) {
return ANY
}
const segmentName = segments[0]
return {
hasProperty: (name) => name === segmentName,
allProperties: () => new Map([[segmentName, { nodes: [node] }]]),
getNest: (name) =>
name === segmentName ? extractFromSegments(segments.slice(1)) : NEVER
}
}
}
/**
* Extract the property references from name literal.
* @param {Expression} node
* @returns {IPropertyReferences}
*/
function extractFromNameLiteral(node) {
const referenceName =
node.type === 'Literal' || node.type === 'TemplateLiteral'
? utils.getStringLiteralValue(node)
: null
if (referenceName) {
return {
hasProperty: (name) => name === referenceName,
allProperties: () => new Map([[referenceName, { nodes: [node] }]]),
getNest: (name) => (name === referenceName ? ANY : NEVER)
}
} else {
return NEVER
}
}
/**
* Extract the property references from name.
* @param {string} referenceName
* @param {Expression|SpreadElement} nameNode
* @param { () => IPropertyReferences } [getNest]
* @returns {IPropertyReferences}
*/
function extractFromName(referenceName, nameNode, getNest) {
return {
hasProperty: (name) => name === referenceName,
allProperties: () => new Map([[referenceName, { nodes: [nameNode] }]]),
getNest: (name) =>
name === referenceName ? (getNest ? getNest() : ANY) : NEVER
}
}
/**
* Extract the property references from toRef call.
* @param {CallExpression} node
* @returns {IPropertyReferences}
*/
function extractFromToRef(node) {
const nameNode = node.arguments[1]
const refName =
nameNode &&
(nameNode.type === 'Literal' || nameNode.type === 'TemplateLiteral')
? utils.getStringLiteralValue(nameNode)
: null
if (!refName) {
// unknown name
return ANY
}
return extractFromName(refName, nameNode, () => {
return extractFromExpression(node, false).getNest('value')
})
}
/**
* Extract the property references from toRefs call.
* @param {CallExpression} node
* @returns {IPropertyReferences}
*/
function extractFromToRefs(node) {
const base = extractFromExpression(node, false)
return {
hasProperty: (name, option) => base.hasProperty(name, option),
allProperties: () => base.allProperties(),
getNest: (name) => base.getNest(name).getNest('value')
}
}
/**
* Extract the property references from VExpressionContainer.
* @param {VExpressionContainer} node
* @param {object} [options]
* @param {boolean} [options.ignoreGlobals]
* @returns {IPropertyReferences}
*/
function extractFromVExpressionContainer(node, options) {
const ignoreGlobals = options && options.ignoreGlobals
/** @type { (name:string)=>boolean } */
let ignoreRef = () => false
if (ignoreGlobals) {
const globalScope =
context.getSourceCode().scopeManager.globalScope ||
context.getSourceCode().scopeManager.scopes[0]
ignoreRef = (name) => globalScope.set.has(name)
}
/** @type {IPropertyReferences[]} */
const references = []
for (const id of node.references
.filter((ref) => ref.variable == null)
.map((ref) => ref.id)) {
if (ignoreRef(id.name)) {
continue
}
if (!isFunctionalTemplate) {
references.push(
extractFromName(id.name, id, () => extractFromExpression(id, true))
)
} else {
if (id.name === 'props') {
references.push(extractFromExpression(id, true))
}
}
}
return mergePropertyReferences(references)
}
/**
* Extract the property references from StyleVariablesContext.
* @param {StyleVariablesContext} ctx
* @returns {IPropertyReferences}
*/
function extractFromStyleVariablesContext(ctx) {
const references = []
for (const { id } of ctx.references) {
references.push(
extractFromName(id.name, id, () => extractFromExpression(id, true))
)
}
return mergePropertyReferences(references)
}
return {
extractFromExpression,
extractFromPattern,
extractFromFunctionParam,
extractFromPath,
extractFromName,
extractFromNameLiteral,
extractFromVExpressionContainer,
extractFromStyleVariablesContext
}
}
/**
* @param {IPropertyReferences[]} references
* @returns {IPropertyReferences}
*/
function mergePropertyReferences(references) {
if (references.length === 0) {
return NEVER
}
if (references.length === 1) {
return references[0]
}
return new PropertyReferencesForMerge(references)
}
/**
* Collects the property references for merge.
* @implements IPropertyReferences
*/
class PropertyReferencesForMerge {
/**
* @param {IPropertyReferences[]} references
*/
constructor(references) {
this.references = references
}
/**
* @param {string} name
* @param {IHasPropertyOption} [option]
*/
hasProperty(name, option) {
return this.references.some((ref) => ref.hasProperty(name, option))
}
allProperties() {
const result = new Map()
for (const reference of this.references) {
for (const [name, { nodes }] of reference.allProperties()) {
const r = result.get(name)
if (r) {
r.nodes = [...new Set([...r.nodes, ...nodes])]
} else {
result.set(name, { nodes: [...nodes] })
}
}
}
return result
}
/**
* @param {string} name
* @returns {IPropertyReferences}
*/
getNest(name) {
/** @type {IPropertyReferences[]} */
const nest = []
for (const ref of this.references) {
if (ref.hasProperty(name)) {
nest.push(ref.getNest(name))
}
}
return mergePropertyReferences(nest)
}
}

View File

@ -0,0 +1,48 @@
const RE_REGEXP_CHAR = /[\\^$.*+?()[\]{}|]/gu
const RE_HAS_REGEXP_CHAR = new RegExp(RE_REGEXP_CHAR.source)
const RE_REGEXP_STR = /^\/(.+)\/(.*)$/u
/**
* Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
* "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
*
* @param {string} string The string to escape.
* @returns {string} Returns the escaped string.
*/
function escape(string) {
return string && RE_HAS_REGEXP_CHAR.test(string)
? string.replace(RE_REGEXP_CHAR, '\\$&')
: string
}
/**
* Convert a string to the `RegExp`.
* Normal strings (e.g. `"foo"`) is converted to `/^foo$/` of `RegExp`.
* Strings like `"/^foo/i"` are converted to `/^foo/i` of `RegExp`.
*
* @param {string} string The string to convert.
* @returns {RegExp} Returns the `RegExp`.
*/
function toRegExp(string) {
const parts = RE_REGEXP_STR.exec(string)
if (parts) {
return new RegExp(parts[1], parts[2])
}
return new RegExp(`^${escape(string)}$`)
}
/**
* Checks whether given string is regexp string
* @param {string} string
* @returns {boolean}
*/
function isRegExp(string) {
return Boolean(RE_REGEXP_STR.exec(string))
}
module.exports = {
escape,
toRegExp,
isRegExp
}

View File

@ -0,0 +1,605 @@
'use strict'
const parser = require('postcss-selector-parser')
const { default: nthCheck } = require('nth-check')
const { getAttribute, isVElement } = require('.')
/**
* @typedef {object} VElementSelector
* @property {(element: VElement)=>boolean} test
*/
module.exports = {
parseSelector
}
/**
* Parses CSS selectors and returns an object with a function that tests VElement.
* @param {string} selector CSS selector
* @param {RuleContext} context - The rule context.
* @returns {VElementSelector}
*/
function parseSelector(selector, context) {
let astSelector
try {
astSelector = parser().astSync(selector)
} catch (e) {
context.report({
loc: { line: 0, column: 0 },
message: `Cannot parse selector: ${selector}.`
})
return {
test: () => false
}
}
try {
const test = selectorsToVElementMatcher(astSelector.nodes)
return {
test(element) {
return test(element, null)
}
}
} catch (e) {
if (e instanceof SelectorError) {
context.report({
loc: { line: 0, column: 0 },
message: e.message
})
return {
test: () => false
}
}
throw e
}
}
class SelectorError extends Error {}
/**
* @typedef {(element: VElement, subject: VElement | null )=>boolean} VElementMatcher
* @typedef {Exclude<parser.Selector['nodes'][number], {type:'comment'|'root'}>} ChildNode
*/
/**
* Convert nodes to VElementMatcher
* @param {parser.Selector[]} selectorNodes
* @returns {VElementMatcher}
*/
function selectorsToVElementMatcher(selectorNodes) {
const selectors = selectorNodes.map((n) =>
selectorToVElementMatcher(cleanSelectorChildren(n))
)
return (element, subject) => selectors.some((sel) => sel(element, subject))
}
/**
* Clean and get the selector child nodes.
* @param {parser.Selector} selector
* @returns {ChildNode[]}
*/
function cleanSelectorChildren(selector) {
/** @type {ChildNode[]} */
const nodes = []
/** @type {ChildNode|null} */
let last = null
for (const node of selector.nodes) {
if (node.type === 'root') {
throw new SelectorError('Unexpected state type=root')
}
if (node.type === 'comment') {
continue
}
if (
(last == null || last.type === 'combinator') &&
isDescendantCombinator(node)
) {
// Ignore descendant combinator
continue
}
if (isDescendantCombinator(last) && node.type === 'combinator') {
// Replace combinator
nodes.pop()
}
nodes.push(node)
last = node
}
if (isDescendantCombinator(last)) {
nodes.pop()
}
return nodes
/**
* @param {parser.Node|null} node
* @returns {node is parser.Combinator}
*/
function isDescendantCombinator(node) {
return Boolean(node && node.type === 'combinator' && !node.value.trim())
}
}
/**
* Convert Selector child nodes to VElementMatcher
* @param {ChildNode[]} selectorChildren
* @returns {VElementMatcher}
*/
function selectorToVElementMatcher(selectorChildren) {
const nodes = [...selectorChildren]
let node = nodes.shift()
/**
* @type {VElementMatcher | null}
*/
let result = null
while (node) {
if (node.type === 'combinator') {
const combinator = node.value
node = nodes.shift()
if (!node) {
throw new SelectorError(`Expected selector after '${combinator}'.`)
}
if (node.type === 'combinator') {
throw new SelectorError(`Unexpected combinator '${node.value}'.`)
}
const right = nodeToVElementMatcher(node)
result = combination(
result ||
// for :has()
((element, subject) => element === subject),
combinator,
right
)
} else {
const sel = nodeToVElementMatcher(node)
result = result ? compound(result, sel) : sel
}
node = nodes.shift()
}
if (!result) {
throw new SelectorError(`Unexpected empty selector.`)
}
return result
}
/**
* @param {VElementMatcher} left
* @param {string} combinator
* @param {VElementMatcher} right
* @returns {VElementMatcher}
*/
function combination(left, combinator, right) {
switch (combinator.trim()) {
case '':
// descendant
return (element, subject) => {
if (right(element, null)) {
let parent = element.parent
while (parent.type === 'VElement') {
if (left(parent, subject)) {
return true
}
parent = parent.parent
}
}
return false
}
case '>':
// child
return (element, subject) => {
if (right(element, null)) {
const parent = element.parent
if (parent.type === 'VElement') {
return left(parent, subject)
}
}
return false
}
case '+':
// adjacent
return (element, subject) => {
if (right(element, null)) {
const before = getBeforeElement(element)
if (before) {
return left(before, subject)
}
}
return false
}
case '~':
// sibling
return (element, subject) => {
if (right(element, null)) {
for (const before of getBeforeElements(element)) {
if (left(before, subject)) {
return true
}
}
}
return false
}
default:
throw new SelectorError(`Unknown combinator: ${combinator}.`)
}
}
/**
* Convert node to VElementMatcher
* @param {Exclude<parser.Node, {type:'combinator'|'comment'|'root'|'selector'}>} selector
* @returns {VElementMatcher}
*/
function nodeToVElementMatcher(selector) {
switch (selector.type) {
case 'attribute':
return attributeNodeToVElementMatcher(selector)
case 'class':
return classNameNodeToVElementMatcher(selector)
case 'id':
return identifierNodeToVElementMatcher(selector)
case 'tag':
return tagNodeToVElementMatcher(selector)
case 'universal':
return universalNodeToVElementMatcher(selector)
case 'pseudo':
return pseudoNodeToVElementMatcher(selector)
case 'nesting':
throw new SelectorError('Unsupported nesting selector.')
case 'string':
throw new SelectorError(`Unknown selector: ${selector.value}.`)
default:
throw new SelectorError(
`Unknown selector: ${/** @type {any}*/ (selector).value}.`
)
}
}
/**
* Convert Attribute node to VElementMatcher
* @param {parser.Attribute} selector
* @returns {VElementMatcher}
*/
function attributeNodeToVElementMatcher(selector) {
const key = selector.attribute
if (!selector.operator) {
return (element) => getAttributeValue(element, key) != null
}
const value = selector.value || ''
switch (selector.operator) {
case '=':
return buildVElementMatcher(value, (attr, val) => attr === val)
case '~=':
// words
return buildVElementMatcher(value, (attr, val) =>
attr.split(/\s+/gu).includes(val)
)
case '|=':
// immediately followed by hyphen
return buildVElementMatcher(
value,
(attr, val) => attr === val || attr.startsWith(`${val}-`)
)
case '^=':
// prefixed
return buildVElementMatcher(value, (attr, val) => attr.startsWith(val))
case '$=':
// suffixed
return buildVElementMatcher(value, (attr, val) => attr.endsWith(val))
case '*=':
// contains
return buildVElementMatcher(value, (attr, val) => attr.includes(val))
default:
throw new SelectorError(`Unsupported operator: ${selector.operator}.`)
}
/**
* @param {string} selectorValue
* @param {(attrValue:string, selectorValue: string)=>boolean} test
* @returns {VElementMatcher}
*/
function buildVElementMatcher(selectorValue, test) {
const val = selector.insensitive
? selectorValue.toLowerCase()
: selectorValue
return (element) => {
const attrValue = getAttributeValue(element, key)
if (attrValue == null) {
return false
}
return test(
selector.insensitive ? attrValue.toLowerCase() : attrValue,
val
)
}
}
}
/**
* Convert ClassName node to VElementMatcher
* @param {parser.ClassName} selector
* @returns {VElementMatcher}
*/
function classNameNodeToVElementMatcher(selector) {
const className = selector.value
return (element) => {
const attrValue = getAttributeValue(element, 'class')
if (attrValue == null) {
return false
}
return attrValue.split(/\s+/gu).includes(className)
}
}
/**
* Convert Identifier node to VElementMatcher
* @param {parser.Identifier} selector
* @returns {VElementMatcher}
*/
function identifierNodeToVElementMatcher(selector) {
const id = selector.value
return (element) => {
const attrValue = getAttributeValue(element, 'id')
if (attrValue == null) {
return false
}
return attrValue === id
}
}
/**
* Convert Tag node to VElementMatcher
* @param {parser.Tag} selector
* @returns {VElementMatcher}
*/
function tagNodeToVElementMatcher(selector) {
const name = selector.value
return (element) => element.rawName === name
}
/**
* Convert Universal node to VElementMatcher
* @param {parser.Universal} _selector
* @returns {VElementMatcher}
*/
function universalNodeToVElementMatcher(_selector) {
return () => true
}
/**
* Convert Pseudo node to VElementMatcher
* @param {parser.Pseudo} selector
* @returns {VElementMatcher}
*/
function pseudoNodeToVElementMatcher(selector) {
const pseudo = selector.value
switch (pseudo) {
case ':not': {
// https://developer.mozilla.org/en-US/docs/Web/CSS/:not
const selectors = selectorsToVElementMatcher(selector.nodes)
return (element, subject) => {
return !selectors(element, subject)
}
}
case ':is':
case ':where':
// https://developer.mozilla.org/en-US/docs/Web/CSS/:is
// https://developer.mozilla.org/en-US/docs/Web/CSS/:where
return selectorsToVElementMatcher(selector.nodes)
case ':has':
// https://developer.mozilla.org/en-US/docs/Web/CSS/:has
return pseudoHasSelectorsToVElementMatcher(selector.nodes)
case ':empty':
// https://developer.mozilla.org/en-US/docs/Web/CSS/:empty
return (element) =>
element.children.every(
(child) => child.type === 'VText' && !child.value.trim()
)
case ':nth-child': {
// https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child
const nth = parseNth(selector)
return buildPseudoNthVElementMatcher(nth)
}
case ':nth-last-child': {
// https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-child
const nth = parseNth(selector)
return buildPseudoNthVElementMatcher((index, length) =>
nth(length - index - 1)
)
}
case ':first-child':
// https://developer.mozilla.org/en-US/docs/Web/CSS/:first-child
return buildPseudoNthVElementMatcher((index) => index === 0)
case ':last-child':
// https://developer.mozilla.org/en-US/docs/Web/CSS/:last-child
return buildPseudoNthVElementMatcher(
(index, length) => index === length - 1
)
case ':only-child':
// https://developer.mozilla.org/en-US/docs/Web/CSS/:only-child
return buildPseudoNthVElementMatcher(
(index, length) => index === 0 && length === 1
)
case ':nth-of-type': {
// https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-of-type
const nth = parseNth(selector)
return buildPseudoNthOfTypeVElementMatcher(nth)
}
case ':nth-last-of-type': {
// https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-of-type
const nth = parseNth(selector)
return buildPseudoNthOfTypeVElementMatcher((index, length) =>
nth(length - index - 1)
)
}
case ':first-of-type':
// https://developer.mozilla.org/en-US/docs/Web/CSS/:first-of-type
return buildPseudoNthOfTypeVElementMatcher((index) => index === 0)
case ':last-of-type':
// https://developer.mozilla.org/en-US/docs/Web/CSS/:last-of-type
return buildPseudoNthOfTypeVElementMatcher(
(index, length) => index === length - 1
)
case ':only-of-type':
// https://developer.mozilla.org/en-US/docs/Web/CSS/:only-of-type
return buildPseudoNthOfTypeVElementMatcher(
(index, length) => index === 0 && length === 1
)
default:
throw new SelectorError(`Unsupported pseudo selector: ${pseudo}.`)
}
}
/**
* Convert :has() selector nodes to VElementMatcher
* @param {parser.Selector[]} selectorNodes
* @returns {VElementMatcher}
*/
function pseudoHasSelectorsToVElementMatcher(selectorNodes) {
const selectors = selectorNodes.map((n) =>
pseudoHasSelectorToVElementMatcher(n)
)
return (element, subject) => selectors.some((sel) => sel(element, subject))
}
/**
* Convert :has() selector node to VElementMatcher
* @param {parser.Selector} selector
* @returns {VElementMatcher}
*/
function pseudoHasSelectorToVElementMatcher(selector) {
const nodes = cleanSelectorChildren(selector)
const selectors = selectorToVElementMatcher(nodes)
const firstNode = nodes[0]
if (
firstNode.type === 'combinator' &&
(firstNode.value === '+' || firstNode.value === '~')
) {
// adjacent or sibling
return buildVElementMatcher(selectors, (element) =>
getAfterElements(element)
)
}
// descendant or child
return buildVElementMatcher(selectors, (element) =>
element.children.filter(isVElement)
)
/**
* @param {VElementMatcher} selectors
* @param {(element: VElement) => VElement[]} getStartElements
* @returns {VElementMatcher}
*/
function buildVElementMatcher(selectors, getStartElements) {
return (element) => {
const elements = [...getStartElements(element)]
/** @type {VElement|undefined} */
let curr
while ((curr = elements.shift())) {
const el = curr
if (selectors(el, element)) {
return true
}
elements.push(...el.children.filter(isVElement))
}
return false
}
}
}
/**
* Parse <nth>
* @param {parser.Pseudo} pseudoNode
* @returns {(index: number)=>boolean}
*/
function parseNth(pseudoNode) {
const argumentsText = pseudoNode
.toString()
.slice(pseudoNode.value.length)
.toLowerCase()
const openParenIndex = argumentsText.indexOf('(')
const closeParenIndex = argumentsText.lastIndexOf(')')
if (openParenIndex < 0 || closeParenIndex < 0) {
throw new SelectorError(
`Cannot parse An+B micro syntax (:nth-xxx() argument): ${argumentsText}.`
)
}
const argument = argumentsText
.slice(openParenIndex + 1, closeParenIndex)
.trim()
try {
return nthCheck(argument)
} catch (e) {
throw new SelectorError(
`Cannot parse An+B micro syntax (:nth-xxx() argument): '${argument}'.`
)
}
}
/**
* Build VElementMatcher for :nth-xxx()
* @param {(index: number, length: number)=>boolean} testIndex
* @returns {VElementMatcher}
*/
function buildPseudoNthVElementMatcher(testIndex) {
return (element) => {
const elements = element.parent.children.filter(isVElement)
return testIndex(elements.indexOf(element), elements.length)
}
}
/**
* Build VElementMatcher for :nth-xxx-of-type()
* @param {(index: number, length: number)=>boolean} testIndex
* @returns {VElementMatcher}
*/
function buildPseudoNthOfTypeVElementMatcher(testIndex) {
return (element) => {
const elements = element.parent.children
.filter(isVElement)
.filter((e) => e.rawName === element.rawName)
return testIndex(elements.indexOf(element), elements.length)
}
}
/**
* @param {VElement} element
*/
function getBeforeElement(element) {
return getBeforeElements(element).pop() || null
}
/**
* @param {VElement} element
*/
function getBeforeElements(element) {
const parent = element.parent
const index = parent.children.indexOf(element)
return parent.children.slice(0, index).filter(isVElement)
}
/**
* @param {VElement} element
*/
function getAfterElements(element) {
const parent = element.parent
const index = parent.children.indexOf(element)
return parent.children.slice(index + 1).filter(isVElement)
}
/**
* @param {VElementMatcher} a
* @param {VElementMatcher} b
* @returns {VElementMatcher}
*/
function compound(a, b) {
return (element, subject) => a(element, subject) && b(element, subject)
}
/**
* Get attribute value from given element.
* @param {VElement} element The element node.
* @param {string} attribute The attribute name.
*/
function getAttributeValue(element, attribute) {
const attr = getAttribute(element, attribute)
if (attr) {
return (attr.value && attr.value.value) || ''
}
return null
}

View File

@ -0,0 +1,62 @@
const { isVElement } = require('..')
class StyleVariablesContext {
/**
* @param {RuleContext} context
* @param {VElement[]} styles
*/
constructor(context, styles) {
this.context = context
this.styles = styles
/** @type {VReference[]} */
this.references = []
/** @type {VExpressionContainer[]} */
this.vBinds = []
for (const style of styles) {
for (const node of style.children) {
if (node.type === 'VExpressionContainer') {
this.vBinds.push(node)
for (const ref of node.references.filter(
(ref) => ref.variable == null
)) {
this.references.push(ref)
}
}
}
}
}
}
module.exports = {
getStyleVariablesContext,
StyleVariablesContext
}
/** @type {WeakMap<VElement, StyleVariablesContext>} */
const cache = new WeakMap()
/**
* Get the style vars context
* @param {RuleContext} context
* @returns {StyleVariablesContext | null}
*/
function getStyleVariablesContext(context) {
const df =
context.parserServices.getDocumentFragment &&
context.parserServices.getDocumentFragment()
if (!df) {
return null
}
const styles = df.children
.filter(isVElement)
.filter((e) => e.name === 'style')
if (!styles.length) {
return null
}
let ctx = cache.get(styles[0])
if (ctx) {
return ctx
}
ctx = new StyleVariablesContext(context, styles)
cache.set(styles[0], ctx)
return ctx
}

View File

@ -0,0 +1,138 @@
[
"accent-height",
"alignment-baseline",
"arabic-form",
"attributeName",
"attributeType",
"baseFrequency",
"baseline-shift",
"baseProfile",
"calcMode",
"cap-height",
"clipPathUnits",
"clip-path",
"clip-rule",
"color-interpolation",
"color-interpolation-filters",
"color-profile",
"color-rendering",
"contentScriptType",
"contentStyleType",
"diffuseConstant",
"dominant-baseline",
"edgeMode",
"enable-background",
"externalResourcesRequired",
"fill-opacity",
"fill-rule",
"filterRes",
"filterUnits",
"flood-color",
"flood-opacity",
"font-family",
"font-size",
"font-size-adjust",
"font-stretch",
"font-style",
"font-variant",
"font-weight",
"glyph-name",
"glyph-orientation-horizontal",
"glyph-orientation-vertical",
"glyphRef",
"gradientTransform",
"gradientUnits",
"horiz-adv-x",
"horiz-origin-x",
"image-rendering",
"kernelMatrix",
"kernelUnitLength",
"keyPoints",
"keySplines",
"keyTimes",
"lengthAdjust",
"letter-spacing",
"lighting-color",
"limitingConeAngle",
"marker-end",
"marker-mid",
"marker-start",
"markerHeight",
"markerUnits",
"markerWidth",
"maskContentUnits",
"maskUnits",
"numOctaves",
"overline-position",
"overline-thickness",
"panose-1",
"paint-order",
"pathLength",
"patternContentUnits",
"patternTransform",
"patternUnits",
"pointer-events",
"pointsAtX",
"pointsAtY",
"pointsAtZ",
"preserveAlpha",
"preserveAspectRatio",
"primitiveUnits",
"referrerPolicy",
"refX",
"refY",
"rendering-intent",
"repeatCount",
"repeatDur",
"requiredExtensions",
"requiredFeatures",
"shape-rendering",
"specularConstant",
"specularExponent",
"spreadMethod",
"startOffset",
"stdDeviation",
"stitchTiles",
"stop-color",
"stop-opacity",
"strikethrough-position",
"strikethrough-thickness",
"stroke-dasharray",
"stroke-dashoffset",
"stroke-linecap",
"stroke-linejoin",
"stroke-miterlimit",
"stroke-opacity",
"stroke-width",
"surfaceScale",
"systemLanguage",
"tableValues",
"targetX",
"targetY",
"text-anchor",
"text-decoration",
"text-rendering",
"textLength",
"transform-origin",
"underline-position",
"underline-thickness",
"unicode-bidi",
"unicode-range",
"units-per-em",
"v-alphabetic",
"v-hanging",
"v-ideographic",
"v-mathematical",
"vector-effect",
"vert-adv-y",
"vert-origin-x",
"vert-origin-y",
"viewBox",
"viewTarget",
"word-spacing",
"writing-mode",
"x-height",
"xChannelSelector",
"yChannelSelector",
"zoomAndPan"
]

View File

@ -0,0 +1 @@
["a","animate","animateMotion","animateTransform","audio","canvas","circle","clipPath","defs","desc","discard","ellipse","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence","filter","foreignObject","g","iframe","image","line","linearGradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialGradient","rect","script","set","stop","style","svg","switch","symbol","text","textPath","title","tspan","unknown","use","video","view"]

View File

@ -0,0 +1,333 @@
const { findVariable } = require('eslint-utils')
/**
* @typedef {import('@typescript-eslint/types').TSESTree.TypeNode} TypeNode
* @typedef {import('@typescript-eslint/types').TSESTree.TSInterfaceBody} TSInterfaceBody
* @typedef {import('@typescript-eslint/types').TSESTree.TSTypeLiteral} TSTypeLiteral
* @typedef {import('@typescript-eslint/types').TSESTree.Parameter} TSESTreeParameter
* @typedef {import('@typescript-eslint/types').TSESTree.Node} Node
*
*/
/**
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentTypeProp} ComponentTypeProp
* @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').ComponentTypeEmit} ComponentTypeEmit
*/
module.exports = {
isTypeNode,
getComponentPropsFromTypeDefine,
getComponentEmitsFromTypeDefine
}
/**
* @param {Node | ASTNode} node
* @returns {node is TypeNode}
*/
function isTypeNode(node) {
return (
node.type === 'TSAnyKeyword' ||
node.type === 'TSArrayType' ||
node.type === 'TSBigIntKeyword' ||
node.type === 'TSBooleanKeyword' ||
node.type === 'TSConditionalType' ||
node.type === 'TSConstructorType' ||
node.type === 'TSFunctionType' ||
node.type === 'TSImportType' ||
node.type === 'TSIndexedAccessType' ||
node.type === 'TSInferType' ||
node.type === 'TSIntersectionType' ||
node.type === 'TSIntrinsicKeyword' ||
node.type === 'TSLiteralType' ||
node.type === 'TSMappedType' ||
node.type === 'TSNamedTupleMember' ||
node.type === 'TSNeverKeyword' ||
node.type === 'TSNullKeyword' ||
node.type === 'TSNumberKeyword' ||
node.type === 'TSObjectKeyword' ||
node.type === 'TSOptionalType' ||
node.type === 'TSRestType' ||
node.type === 'TSStringKeyword' ||
node.type === 'TSSymbolKeyword' ||
node.type === 'TSTemplateLiteralType' ||
node.type === 'TSThisType' ||
node.type === 'TSTupleType' ||
node.type === 'TSTypeLiteral' ||
node.type === 'TSTypeOperator' ||
node.type === 'TSTypePredicate' ||
node.type === 'TSTypeQuery' ||
node.type === 'TSTypeReference' ||
node.type === 'TSUndefinedKeyword' ||
node.type === 'TSUnionType' ||
node.type === 'TSUnknownKeyword' ||
node.type === 'TSVoidKeyword'
)
}
/**
* @param {TypeNode} node
* @returns {node is TSTypeLiteral}
*/
function isTSTypeLiteral(node) {
return node.type === 'TSTypeLiteral'
}
/**
* @param {TypeNode} node
* @returns {node is TSFunctionType}
*/
function isTSFunctionType(node) {
return node.type === 'TSFunctionType'
}
/**
* Get all props by looking at all component's properties
* @param {RuleContext} context The ESLint rule context object.
* @param {TypeNode} propsNode Type with props definition
* @return {ComponentTypeProp[]} Array of component props
*/
function getComponentPropsFromTypeDefine(context, propsNode) {
/** @type {TSInterfaceBody | TSTypeLiteral|null} */
const defNode = resolveQualifiedType(context, propsNode, isTSTypeLiteral)
if (!defNode) {
return []
}
return [...extractRuntimeProps(context, defNode)]
}
/**
* Get all emits by looking at all component's properties
* @param {RuleContext} context The ESLint rule context object.
* @param {TypeNode} emitsNode Type with emits definition
* @return {ComponentTypeEmit[]} Array of component emits
*/
function getComponentEmitsFromTypeDefine(context, emitsNode) {
/** @type {TSInterfaceBody | TSTypeLiteral | TSFunctionType | null} */
const defNode = resolveQualifiedType(
context,
emitsNode,
(n) => isTSTypeLiteral(n) || isTSFunctionType(n)
)
if (!defNode) {
return []
}
return [...extractRuntimeEmits(defNode)]
}
/**
* @see https://github.com/vuejs/vue-next/blob/253ca2729d808fc051215876aa4af986e4caa43c/packages/compiler-sfc/src/compileScript.ts#L1512
* @param {RuleContext} context The ESLint rule context object.
* @param {TSTypeLiteral | TSInterfaceBody} node
* @returns {IterableIterator<ComponentTypeProp>}
*/
function* extractRuntimeProps(context, node) {
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
for (const m of members) {
if (
(m.type === 'TSPropertySignature' || m.type === 'TSMethodSignature') &&
(m.key.type === 'Identifier' || m.key.type === 'Literal')
) {
let type
if (m.type === 'TSMethodSignature') {
type = ['Function']
} else if (m.typeAnnotation) {
type = inferRuntimeType(context, m.typeAnnotation.typeAnnotation)
}
yield {
type: 'type',
key: /** @type {Identifier | Literal} */ (m.key),
propName: m.key.type === 'Identifier' ? m.key.name : `${m.key.value}`,
value: null,
node: /** @type {TSPropertySignature | TSMethodSignature} */ (m),
required: !m.optional,
types: type || [`null`]
}
}
}
}
/**
* @see https://github.com/vuejs/vue-next/blob/348c3b01e56383ffa70b180d1376fdf4ac12e274/packages/compiler-sfc/src/compileScript.ts#L1632
* @param {TSTypeLiteral | TSInterfaceBody | TSFunctionType} node
* @returns {IterableIterator<ComponentTypeEmit>}
*/
function* extractRuntimeEmits(node) {
if (node.type === 'TSTypeLiteral' || node.type === 'TSInterfaceBody') {
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
for (const t of members) {
if (t.type === 'TSCallSignatureDeclaration') {
yield* extractEventNames(
t.params[0],
/** @type {TSCallSignatureDeclaration} */ (t)
)
}
}
return
} else {
yield* extractEventNames(node.params[0], node)
}
/**
* @param {TSESTreeParameter} eventName
* @param {TSCallSignatureDeclaration | TSFunctionType} member
* @returns {IterableIterator<ComponentTypeEmit>}
*/
function* extractEventNames(eventName, member) {
if (
eventName &&
eventName.type === 'Identifier' &&
eventName.typeAnnotation &&
eventName.typeAnnotation.type === 'TSTypeAnnotation'
) {
const typeNode = eventName.typeAnnotation.typeAnnotation
if (
typeNode.type === 'TSLiteralType' &&
typeNode.literal.type === 'Literal'
) {
const emitName = String(typeNode.literal.value)
yield {
type: 'type',
key: /** @type {TSLiteralType} */ (typeNode),
emitName,
value: null,
node: member
}
} else if (typeNode.type === 'TSUnionType') {
for (const t of typeNode.types) {
if (t.type === 'TSLiteralType' && t.literal.type === 'Literal') {
const emitName = String(t.literal.value)
yield {
type: 'type',
key: /** @type {TSLiteralType} */ (t),
emitName,
value: null,
node: member
}
}
}
}
}
}
}
/**
* @see https://github.com/vuejs/vue-next/blob/253ca2729d808fc051215876aa4af986e4caa43c/packages/compiler-sfc/src/compileScript.ts#L425
*
* @param {RuleContext} context The ESLint rule context object.
* @param {TypeNode} node
* @param {(n: TypeNode)=> boolean } qualifier
*/
function resolveQualifiedType(context, node, qualifier) {
if (qualifier(node)) {
return node
}
if (node.type === 'TSTypeReference' && node.typeName.type === 'Identifier') {
const refName = node.typeName.name
const variable = findVariable(context.getScope(), refName)
if (variable && variable.defs.length === 1) {
const def = variable.defs[0]
if (def.node.type === 'TSInterfaceDeclaration') {
return /** @type {any} */ (def.node).body
}
if (def.node.type === 'TSTypeAliasDeclaration') {
const typeAnnotation = /** @type {any} */ (def.node).typeAnnotation
return qualifier(typeAnnotation) ? typeAnnotation : null
}
}
}
}
/**
* @param {RuleContext} context The ESLint rule context object.
* @param {TypeNode} node
* @param {Set<TypeNode>} [checked]
* @returns {string[]}
*/
function inferRuntimeType(context, node, checked = new Set()) {
switch (node.type) {
case 'TSStringKeyword':
return ['String']
case 'TSNumberKeyword':
return ['Number']
case 'TSBooleanKeyword':
return ['Boolean']
case 'TSObjectKeyword':
return ['Object']
case 'TSTypeLiteral':
return ['Object']
case 'TSFunctionType':
return ['Function']
case 'TSArrayType':
case 'TSTupleType':
return ['Array']
case 'TSLiteralType':
if (node.literal.type === 'Literal') {
switch (typeof node.literal.value) {
case 'boolean':
return ['Boolean']
case 'string':
return ['String']
case 'number':
case 'bigint':
return ['Number']
}
if (node.literal.value instanceof RegExp) {
return ['RegExp']
}
}
return [`null`]
case 'TSTypeReference':
if (node.typeName.type === 'Identifier') {
const variable = findVariable(context.getScope(), node.typeName.name)
if (variable && variable.defs.length === 1) {
const def = variable.defs[0]
if (def.node.type === 'TSInterfaceDeclaration') {
return [`Object`]
}
if (def.node.type === 'TSTypeAliasDeclaration') {
const typeAnnotation = /** @type {any} */ (def.node).typeAnnotation
if (!checked.has(typeAnnotation)) {
checked.add(typeAnnotation)
return inferRuntimeType(context, typeAnnotation, checked)
}
}
}
switch (node.typeName.name) {
case 'Array':
case 'Function':
case 'Object':
case 'Set':
case 'Map':
case 'WeakSet':
case 'WeakMap':
case 'Date':
return [node.typeName.name]
case 'Record':
case 'Partial':
case 'Readonly':
case 'Pick':
case 'Omit':
case 'Exclude':
case 'Extract':
case 'Required':
case 'InstanceType':
return ['Object']
}
}
return [`null`]
case 'TSUnionType':
const set = new Set()
for (const t of node.types) {
for (const tt of inferRuntimeType(context, t, checked)) {
set.add(tt)
}
}
return [...set]
case 'TSIntersectionType':
return ['Object']
default:
return [`null`] // no runtime check
}
}

View File

@ -0,0 +1 @@
["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"]

View File

@ -0,0 +1,54 @@
{
"nuxt": ["asyncData", "fetch", "head", "key", "layout", "loading", "middleware", "scrollToTop", "transition", "validate", "watchQuery"],
"vue-router": [
"beforeRouteEnter",
"beforeRouteUpdate",
"beforeRouteLeave"
],
"vue": [
"data",
"props",
"propsData",
"computed",
"methods",
"watch",
"el",
"template",
"render",
"renderError",
"staticRenderFns",
"beforeCreate",
"created",
"beforeDestroy",
"destroyed",
"beforeMount",
"mounted",
"beforeUpdate",
"updated",
"activated",
"deactivated",
"errorCaptured",
"serverPrefetch",
"directives",
"components",
"transitions",
"filters",
"provide",
"inject",
"model",
"parent",
"mixins",
"name",
"extends",
"delimiters",
"comments",
"inheritAttrs",
"setup",
"emits",
"beforeUnmount",
"unmounted",
"renderTracked",
"renderTriggered"
]
}

View File

@ -0,0 +1,4 @@
[
"$data", "$props", "$el", "$options", "$parent", "$root", "$children", "$slots", "$scopedSlots", "$refs", "$isServer", "$attrs", "$listeners",
"$watch", "$set", "$delete", "$on", "$once", "$off", "$emit", "$mount", "$forceUpdate", "$nextTick", "$destroy"
]

View File

@ -0,0 +1,12 @@
module.exports = [
'template',
'slot',
'component',
'Component',
'transition',
'Transition',
'transition-group',
'TransitionGroup',
'keep-alive',
'KeepAlive'
]

View File

@ -0,0 +1,7 @@
module.exports = [
...require('./vue2-builtin-components'),
'teleport',
'Teleport',
'suspense',
'Suspense'
]

View File

@ -0,0 +1,300 @@
[
"compile",
"createApp",
"createSSRApp",
"defineCustomElement",
"defineSSRCustomElement",
"hydrate",
"render",
"Transition",
"TransitionGroup",
"TransitionGroupProps",
"TransitionProps",
"useCssModule",
"useCssVars",
"vModelCheckbox",
"vModelDynamic",
"vModelRadio",
"vModelSelect",
"vModelText",
"vShow",
"VueElement",
"VueElementConstructor",
"withKeys",
"withModifiers",
"AllowedComponentProps",
"App",
"AppConfig",
"AppContext",
"AsyncComponentLoader",
"AsyncComponentOptions",
"BaseTransition",
"BaseTransitionProps",
"callWithAsyncErrorHandling",
"callWithErrorHandling",
"camelize",
"capitalize",
"cloneVNode",
"Comment",
"CompatVue",
"Component",
"ComponentCustomOptions",
"ComponentCustomProperties",
"ComponentCustomProps",
"ComponentInternalInstance",
"ComponentObjectPropsOptions",
"ComponentOptions",
"ComponentOptionsBase",
"ComponentOptionsMixin",
"ComponentOptionsWithArrayProps",
"ComponentOptionsWithObjectProps",
"ComponentOptionsWithoutProps",
"ComponentPropsOptions",
"ComponentPublicInstance",
"computed",
"ComputedGetter",
"ComputedOptions",
"ComputedRef",
"ComputedSetter",
"ConcreteComponent",
"CreateAppFunction",
"createBlock",
"createCommentVNode",
"CreateComponentPublicInstance",
"createElementBlock",
"createElementVNode",
"createHydrationRenderer",
"createRenderer",
"createSlots",
"createStaticVNode",
"createTextVNode",
"createVNode",
"customRef",
"CustomRefFactory",
"DebuggerEvent",
"DebuggerEventExtraInfo",
"DebuggerOptions",
"DeepReadonly",
"defineAsyncComponent",
"DefineComponent",
"defineComponent",
"defineEmits",
"defineExpose",
"defineProps",
"DeprecationTypes",
"devtools",
"Directive",
"DirectiveArguments",
"DirectiveBinding",
"DirectiveHook",
"effect",
"EffectScheduler",
"EffectScope",
"effectScope",
"EmitsOptions",
"ErrorCodes",
"ExtractDefaultPropTypes",
"ExtractPropTypes",
"Fragment",
"FunctionalComponent",
"FunctionDirective",
"getCurrentInstance",
"getCurrentScope",
"getTransitionRawChildren",
"guardReactiveProps",
"h",
"handleError",
"HMRRuntime",
"HydrationRenderer",
"initCustomFormatter",
"inject",
"InjectionKey",
"isMemoSame",
"isProxy",
"isReactive",
"isReadonly",
"isRef",
"isRuntimeOnly",
"isShallow",
"isVNode",
"KeepAlive",
"KeepAliveProps",
"LegacyConfig",
"markRaw",
"mergeProps",
"MethodOptions",
"nextTick",
"normalizeClass",
"normalizeProps",
"normalizeStyle",
"ObjectDirective",
"ObjectEmitsOptions",
"onActivated",
"onBeforeMount",
"onBeforeUnmount",
"onBeforeUpdate",
"onDeactivated",
"onErrorCaptured",
"onMounted",
"onRenderTracked",
"onRenderTriggered",
"onScopeDispose",
"onServerPrefetch",
"onUnmounted",
"onUpdated",
"openBlock",
"OptionMergeFunction",
"Plugin",
"popScopeId",
"Prop",
"PropType",
"provide",
"proxyRefs",
"pushScopeId",
"queuePostFlushCb",
"reactive",
"ReactiveEffect",
"ReactiveEffectOptions",
"ReactiveEffectRunner",
"ReactiveFlags",
"readonly",
"Ref",
"ref",
"registerRuntimeCompiler",
"Renderer",
"RendererElement",
"RendererNode",
"RendererOptions",
"RenderFunction",
"renderList",
"renderSlot",
"resolveComponent",
"resolveDirective",
"resolveDynamicComponent",
"resolveTransitionHooks",
"RootHydrateFunction",
"RootRenderFunction",
"RuntimeCompilerOptions",
"setBlockTracking",
"setDevtoolsHook",
"setTransitionHooks",
"SetupContext",
"ShallowReactive",
"shallowReactive",
"shallowReadonly",
"ShallowRef",
"shallowRef",
"ShallowUnwrapRef",
"Slot",
"Slots",
"ssrContextKey",
"Static",
"stop",
"Suspense",
"SuspenseBoundary",
"SuspenseProps",
"Teleport",
"TeleportProps",
"Text",
"toDisplayString",
"toHandlerKey",
"toHandlers",
"toRaw",
"ToRef",
"toRef",
"ToRefs",
"toRefs",
"TrackOpTypes",
"transformVNodeArgs",
"TransitionHooks",
"TransitionState",
"TriggerOpTypes",
"triggerRef",
"unref",
"UnwrapNestedRefs",
"UnwrapRef",
"useAttrs",
"useSlots",
"useSSRContext",
"useTransitionState",
"version",
"VNode",
"VNodeArrayChildren",
"VNodeChild",
"VNodeNormalizedChildren",
"VNodeProps",
"VNodeTypes",
"warn",
"watch",
"WatchCallback",
"WatchEffect",
"watchEffect",
"WatchOptions",
"WatchOptionsBase",
"watchPostEffect",
"WatchSource",
"WatchStopHandle",
"watchSyncEffect",
"withCtx",
"withDefaults",
"withDirectives",
"withMemo",
"withScopeId",
"WritableComputedOptions",
"WritableComputedRef",
"CSSProperties",
"StyleValue",
"HTMLAttributes",
"AnchorHTMLAttributes",
"AreaHTMLAttributes",
"AudioHTMLAttributes",
"BaseHTMLAttributes",
"BlockquoteHTMLAttributes",
"ButtonHTMLAttributes",
"CanvasHTMLAttributes",
"ColHTMLAttributes",
"ColgroupHTMLAttributes",
"DataHTMLAttributes",
"DetailsHTMLAttributes",
"DelHTMLAttributes",
"DialogHTMLAttributes",
"EmbedHTMLAttributes",
"FieldsetHTMLAttributes",
"FormHTMLAttributes",
"HtmlHTMLAttributes",
"IframeHTMLAttributes",
"ImgHTMLAttributes",
"InsHTMLAttributes",
"InputHTMLAttributes",
"KeygenHTMLAttributes",
"LabelHTMLAttributes",
"LiHTMLAttributes",
"LinkHTMLAttributes",
"MapHTMLAttributes",
"MenuHTMLAttributes",
"MediaHTMLAttributes",
"MetaHTMLAttributes",
"MeterHTMLAttributes",
"QuoteHTMLAttributes",
"ObjectHTMLAttributes",
"OlHTMLAttributes",
"OptgroupHTMLAttributes",
"OptionHTMLAttributes",
"OutputHTMLAttributes",
"ParamHTMLAttributes",
"ProgressHTMLAttributes",
"ScriptHTMLAttributes",
"SelectHTMLAttributes",
"SourceHTMLAttributes",
"StyleHTMLAttributes",
"TableHTMLAttributes",
"TextareaHTMLAttributes",
"TdHTMLAttributes",
"ThHTMLAttributes",
"TimeHTMLAttributes",
"TrackHTMLAttributes",
"VideoHTMLAttributes",
"WebViewHTMLAttributes",
"SVGAttributes",
"Events"
]