1+ import {
2+ ArrayExpression ,
3+ Node ,
4+ ObjectExpression ,
5+ Statement
6+ } from '@babel/types'
7+ import { BindingMetadata } from '@vue/compiler-dom'
8+ import { CSSVarsBindingTypes } from "./utils" ;
9+
10+ /**
11+ * Analyze bindings in normal `<script>`
12+ * Note that `compileScriptSetup` already analyzes bindings as part of its
13+ * compilation process so this should only be used on single `<script>` SFCs.
14+ */
15+ export function analyzeScriptBindings ( ast : Statement [ ] ) : BindingMetadata {
16+ for ( const node of ast ) {
17+ if (
18+ node . type === 'ExportDefaultDeclaration' &&
19+ node . declaration . type === 'ObjectExpression'
20+ ) {
21+ return analyzeBindingsFromOptions ( node . declaration )
22+ }
23+ }
24+ return { }
25+ }
26+
27+ function analyzeBindingsFromOptions ( node : ObjectExpression ) : BindingMetadata {
28+ const bindings : BindingMetadata = { }
29+ // #3270, #3275
30+ // mark non-script-setup so we don't resolve components/directives from these
31+ Object . defineProperty ( bindings , '__isScriptSetup' , {
32+ enumerable : false ,
33+ value : false
34+ } )
35+ for ( const property of node . properties ) {
36+ if (
37+ property . type === 'ObjectProperty' &&
38+ ! property . computed &&
39+ property . key . type === 'Identifier'
40+ ) {
41+ // props
42+ if ( property . key . name === 'props' ) {
43+ // props: ['foo']
44+ // props: { foo: ... }
45+ for ( const key of getObjectOrArrayExpressionKeys ( property . value ) ) {
46+ bindings [ key ] = CSSVarsBindingTypes . PROPS
47+ }
48+ }
49+
50+ // inject
51+ else if ( property . key . name === 'inject' ) {
52+ // inject: ['foo']
53+ // inject: { foo: {} }
54+ for ( const key of getObjectOrArrayExpressionKeys ( property . value ) ) {
55+ bindings [ key ] = CSSVarsBindingTypes . OPTIONS
56+ }
57+ }
58+
59+ // computed & methods
60+ else if (
61+ property . value . type === 'ObjectExpression' &&
62+ ( property . key . name === 'computed' || property . key . name === 'methods' )
63+ ) {
64+ // methods: { foo() {} }
65+ // computed: { foo() {} }
66+ for ( const key of getObjectExpressionKeys ( property . value ) ) {
67+ bindings [ key ] = CSSVarsBindingTypes . OPTIONS
68+ }
69+ }
70+ }
71+
72+ // setup & data
73+ else if (
74+ property . type === 'ObjectMethod' &&
75+ property . key . type === 'Identifier' &&
76+ ( property . key . name === 'setup' || property . key . name === 'data' )
77+ ) {
78+ for ( const bodyItem of property . body . body ) {
79+ // setup() {
80+ // return {
81+ // foo: null
82+ // }
83+ // }
84+ if (
85+ bodyItem . type === 'ReturnStatement' &&
86+ bodyItem . argument &&
87+ bodyItem . argument . type === 'ObjectExpression'
88+ ) {
89+ for ( const key of getObjectExpressionKeys ( bodyItem . argument ) ) {
90+ bindings [ key ] =
91+ property . key . name === 'setup'
92+ ? CSSVarsBindingTypes . SETUP_MAYBE_REF
93+ : CSSVarsBindingTypes . DATA
94+ }
95+ }
96+ }
97+ }
98+ }
99+
100+ return bindings
101+ }
102+
103+ function getObjectExpressionKeys ( node : ObjectExpression ) : string [ ] {
104+ const keys = [ ]
105+ for ( const prop of node . properties ) {
106+ if ( prop . type === 'SpreadElement' ) continue
107+ const key = resolveObjectKey ( prop . key , prop . computed )
108+ if ( key ) keys . push ( String ( key ) )
109+ }
110+ return keys
111+ }
112+
113+ function getArrayExpressionKeys ( node : ArrayExpression ) : string [ ] {
114+ const keys = [ ]
115+ for ( const element of node . elements ) {
116+ if ( element && element . type === 'StringLiteral' ) {
117+ keys . push ( element . value )
118+ }
119+ }
120+ return keys
121+ }
122+
123+ export function getObjectOrArrayExpressionKeys ( value : Node ) : string [ ] {
124+ if ( value . type === 'ArrayExpression' ) {
125+ return getArrayExpressionKeys ( value )
126+ }
127+ if ( value . type === 'ObjectExpression' ) {
128+ return getObjectExpressionKeys ( value )
129+ }
130+ return [ ]
131+ }
132+
133+ export function resolveObjectKey ( node : Node , computed : boolean ) {
134+ switch ( node . type ) {
135+ case 'StringLiteral' :
136+ case 'NumericLiteral' :
137+ return String ( node . value )
138+ case 'Identifier' :
139+ if ( ! computed ) return node . name
140+ }
141+ return undefined
142+ }
0 commit comments