/
estree-util-to-id-or-member-expression.js
108 lines (95 loc) · 2.95 KB
/
estree-util-to-id-or-member-expression.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* @typedef {import('estree-jsx').Identifier} Identifier
* @typedef {import('estree-jsx').Literal} Literal
* @typedef {import('estree-jsx').JSXIdentifier} JSXIdentifier
* @typedef {import('estree-jsx').MemberExpression} MemberExpression
* @typedef {import('estree-jsx').JSXMemberExpression} JSXMemberExpression
*/
import {
start as esStart,
cont as esCont,
name as isIdentifierName
} from 'estree-util-is-identifier-name'
export const toIdOrMemberExpression = toIdOrMemberExpressionFactory(
'Identifier',
'MemberExpression',
isIdentifierName
)
export const toJsxIdOrMemberExpression =
// @ts-expect-error: fine
/** @type {(ids: Array<string|number>) => JSXIdentifier|JSXMemberExpression)} */
(
toIdOrMemberExpressionFactory(
'JSXIdentifier',
'JSXMemberExpression',
isJsxIdentifierName
)
)
/**
* @param {string} idType
* @param {string} memberType
* @param {(value: string) => boolean} isIdentifier
*/
function toIdOrMemberExpressionFactory(idType, memberType, isIdentifier) {
return toIdOrMemberExpression
/**
* @param {Array<string|number>} ids
* @returns {Identifier|MemberExpression}
*/
function toIdOrMemberExpression(ids) {
let index = -1
/** @type {Identifier|Literal|MemberExpression|undefined} */
let object
while (++index < ids.length) {
const name = ids[index]
const valid = typeof name === 'string' && isIdentifier(name)
// A value of `asd.123` could be turned into `asd['123']` in the JS form,
// but JSX does not have a form for it, so throw.
/* c8 ignore next 3 */
if (idType === 'JSXIdentifier' && !valid) {
throw new Error('Cannot turn `' + name + '` into a JSX identifier')
}
/** @type {Identifier|Literal} */
// @ts-expect-error: JSX is fine.
const id = valid ? {type: idType, name} : {type: 'Literal', value: name}
// @ts-expect-error: JSX is fine.
object = object
? {
type: memberType,
object,
property: id,
computed: id.type === 'Literal',
optional: false
}
: id
}
// Just for types.
/* c8 ignore next 3 */
if (!object) throw new Error('Expected non-empty `ids` to be passed')
if (object.type === 'Literal')
throw new Error('Expected identifier as left-most value')
return object
}
}
/**
* Checks if the given string is a valid JSX identifier name.
* @param {string} name
*/
function isJsxIdentifierName(name) {
let index = -1
while (++index < name.length) {
// We currently receive valid input, but this catches bugs and is needed
// when externalized.
/* c8 ignore next */
if (!(index ? jsxCont : esStart)(name.charCodeAt(index))) return false
}
// `false` if `name` is empty.
return index > 0
}
/**
* Checks if the given character code can continue a JSX identifier.
* @param {number} code
*/
function jsxCont(code) {
return code === 45 /* `-` */ || esCont(code)
}