@@ -12,11 +12,22 @@ import {
12
12
SHARED_RECURSION_TRACKER ,
13
13
UNKNOWN_PATH
14
14
} from '../utils/PathTracker' ;
15
+ import { getRenderedLiteralValue } from '../utils/renderLiteralValue' ;
16
+ import type NamespaceVariable from '../variables/NamespaceVariable' ;
15
17
import ExpressionStatement from './ExpressionStatement' ;
16
18
import type { LiteralValue } from './Literal' ;
17
19
import type * as NodeType from './NodeType' ;
18
- import { type LiteralValueOrUnknown , UnknownValue } from './shared/Expression' ;
19
- import { doNotDeoptimize , type ExpressionNode , NodeBase } from './shared/Node' ;
20
+ import {
21
+ type InclusionOptions ,
22
+ type LiteralValueOrUnknown ,
23
+ UnknownValue
24
+ } from './shared/Expression' ;
25
+ import {
26
+ doNotDeoptimize ,
27
+ type ExpressionNode ,
28
+ type IncludeChildren ,
29
+ NodeBase
30
+ } from './shared/Node' ;
20
31
21
32
type Operator =
22
33
| '!='
@@ -71,13 +82,18 @@ const binaryOperators: Partial<
71
82
// instanceof: () => UnknownValue,
72
83
} ;
73
84
85
+ const UNASSIGNED = Symbol ( 'Unassigned' ) ;
86
+
74
87
export default class BinaryExpression extends NodeBase implements DeoptimizableEntity {
75
88
declare left : ExpressionNode ;
76
89
declare operator : keyof typeof binaryOperators ;
77
90
declare right : ExpressionNode ;
78
91
declare type : NodeType . tBinaryExpression ;
92
+ renderedLiteralValue : string | typeof UnknownValue | typeof UNASSIGNED = UNASSIGNED ;
79
93
80
- deoptimizeCache ( ) : void { }
94
+ deoptimizeCache ( ) : void {
95
+ this . renderedLiteralValue = UnknownValue ;
96
+ }
81
97
82
98
getLiteralValueAtPath (
83
99
path : ObjectPath ,
@@ -88,6 +104,13 @@ export default class BinaryExpression extends NodeBase implements DeoptimizableE
88
104
const leftValue = this . left . getLiteralValueAtPath ( EMPTY_PATH , recursionTracker , origin ) ;
89
105
if ( typeof leftValue === 'symbol' ) return UnknownValue ;
90
106
107
+ // Optimize `'export' in namespace`
108
+ if ( this . operator === 'in' && this . right . variable ?. isNamespace ) {
109
+ return (
110
+ ( this . right . variable as NamespaceVariable ) . context . traceExport ( String ( leftValue ) ) [ 0 ] != null
111
+ ) ;
112
+ }
113
+
91
114
const rightValue = this . right . getLiteralValueAtPath ( EMPTY_PATH , recursionTracker , origin ) ;
92
115
if ( typeof rightValue === 'symbol' ) return UnknownValue ;
93
116
@@ -97,6 +120,16 @@ export default class BinaryExpression extends NodeBase implements DeoptimizableE
97
120
return operatorFunction ( leftValue , rightValue ) ;
98
121
}
99
122
123
+ getRenderedLiteralValue ( ) {
124
+ // Only optimize `'export' in ns`
125
+ if ( this . operator !== 'in' || ! this . right . variable ?. isNamespace ) return UnknownValue ;
126
+
127
+ if ( this . renderedLiteralValue !== UNASSIGNED ) return this . renderedLiteralValue ;
128
+ return ( this . renderedLiteralValue = getRenderedLiteralValue (
129
+ this . getLiteralValueAtPath ( EMPTY_PATH , SHARED_RECURSION_TRACKER , this )
130
+ ) ) ;
131
+ }
132
+
100
133
hasEffects ( context : HasEffectsContext ) : boolean {
101
134
// support some implicit type coercion runtime errors
102
135
if (
@@ -113,9 +146,20 @@ export default class BinaryExpression extends NodeBase implements DeoptimizableE
113
146
return type !== INTERACTION_ACCESSED || path . length > 1 ;
114
147
}
115
148
149
+ include (
150
+ context : InclusionContext ,
151
+ includeChildrenRecursively : IncludeChildren ,
152
+ _options ?: InclusionOptions
153
+ ) {
154
+ this . included = true ;
155
+ if ( typeof this . getRenderedLiteralValue ( ) === 'symbol' ) {
156
+ super . include ( context , includeChildrenRecursively , _options ) ;
157
+ }
158
+ }
159
+
116
160
includeNode ( context : InclusionContext ) {
117
161
this . included = true ;
118
- if ( this . operator === 'in' ) {
162
+ if ( this . operator === 'in' && typeof this . getRenderedLiteralValue ( ) === 'symbol' ) {
119
163
this . right . includePath ( UNKNOWN_PATH , context ) ;
120
164
}
121
165
}
@@ -129,8 +173,13 @@ export default class BinaryExpression extends NodeBase implements DeoptimizableE
129
173
options : RenderOptions ,
130
174
{ renderedSurroundingElement } : NodeRenderOptions = BLANK
131
175
) : void {
132
- this . left . render ( code , options , { renderedSurroundingElement } ) ;
133
- this . right . render ( code , options ) ;
176
+ const renderedLiteralValue = this . getRenderedLiteralValue ( ) ;
177
+ if ( typeof renderedLiteralValue !== 'symbol' ) {
178
+ code . overwrite ( this . start , this . end , renderedLiteralValue ) ;
179
+ } else {
180
+ this . left . render ( code , options , { renderedSurroundingElement } ) ;
181
+ this . right . render ( code , options ) ;
182
+ }
134
183
}
135
184
}
136
185
0 commit comments