55//! config like `run` without needing a Node.js runtime.
66
77use oxc:: {
8- ast:: ast:: {
9- ArrayExpressionElement , Expression , ObjectPropertyKind , Program , PropertyKey , Statement ,
10- } ,
8+ ast:: ast:: { Expression , ObjectPropertyKind , Program , Statement } ,
119 parser:: Parser ,
1210 span:: SourceType ,
1311} ;
@@ -137,7 +135,7 @@ fn extract_default_export_fields(program: &Program<'_>) -> StaticConfig {
137135 match expr {
138136 // Pattern: export default defineConfig({ ... })
139137 Expression :: CallExpression ( call) => {
140- if !is_define_config_call ( & call. callee ) {
138+ if !call. callee . is_specific_id ( "defineConfig" ) {
141139 // Unknown function call — not analyzable
142140 return None ;
143141 }
@@ -162,11 +160,6 @@ fn extract_default_export_fields(program: &Program<'_>) -> StaticConfig {
162160 None
163161}
164162
165- /// Check if a callee expression is `defineConfig`.
166- fn is_define_config_call ( callee : & Expression < ' _ > ) -> bool {
167- matches ! ( callee, Expression :: Identifier ( ident) if ident. name == "defineConfig" )
168- }
169-
170163/// Extract fields from an object expression, converting each value to JSON.
171164/// Fields whose values cannot be represented as pure JSON are recorded as
172165/// [`StaticFieldValue::NonStatic`]. Spread elements and computed properties
@@ -177,48 +170,27 @@ fn extract_object_fields(
177170 let mut map = FxHashMap :: default ( ) ;
178171
179172 for prop in & obj. properties {
180- let ObjectPropertyKind :: ObjectProperty ( prop) = prop else {
173+ if prop. is_spread ( ) {
181174 // Spread elements — keys are unknown at static analysis time
182175 continue ;
183- } ;
184-
185- // Computed properties — keys are unknown at static analysis time
186- if prop. computed {
187- continue ;
188176 }
177+ let ObjectPropertyKind :: ObjectProperty ( prop) = prop else {
178+ continue ;
179+ } ;
189180
190- let Some ( key) = property_key_to_string ( & prop. key ) else {
181+ let Some ( key) = prop. key . static_name ( ) else {
182+ // Computed properties — keys are unknown at static analysis time
191183 continue ;
192184 } ;
193185
194- let value = expr_to_json ( & prop . value )
195- . map_or ( StaticFieldValue :: NonStatic , StaticFieldValue :: Json ) ;
196- map. insert ( key, value) ;
186+ let value =
187+ expr_to_json ( & prop . value ) . map_or ( StaticFieldValue :: NonStatic , StaticFieldValue :: Json ) ;
188+ map. insert ( Box :: from ( key. as_ref ( ) ) , value) ;
197189 }
198190
199191 map
200192}
201193
202- /// Convert a property key to a string.
203- fn property_key_to_string ( key : & PropertyKey < ' _ > ) -> Option < Box < str > > {
204- match key {
205- PropertyKey :: StaticIdentifier ( ident) => Some ( Box :: from ( ident. name . as_str ( ) ) ) ,
206- PropertyKey :: StringLiteral ( lit) => Some ( Box :: from ( lit. value . as_str ( ) ) ) ,
207- PropertyKey :: NumericLiteral ( lit) => {
208- let s = if lit. value . fract ( ) == 0.0 && lit. value . is_finite ( ) {
209- #[ expect( clippy:: cast_possible_truncation) ]
210- {
211- ( lit. value as i64 ) . to_string ( )
212- }
213- } else {
214- lit. value . to_string ( )
215- } ;
216- Some ( Box :: from ( s. as_str ( ) ) )
217- }
218- _ => None ,
219- }
220- }
221-
222194/// Convert an f64 to a JSON value, preserving integers when possible.
223195#[ expect( clippy:: cast_possible_truncation, clippy:: cast_precision_loss) ]
224196fn f64_to_json_number ( value : f64 ) -> serde_json:: Value {
@@ -252,13 +224,8 @@ fn expr_to_json(expr: &Expression<'_>) -> Option<serde_json::Value> {
252224 Expression :: StringLiteral ( lit) => Some ( serde_json:: Value :: String ( lit. value . to_string ( ) ) ) ,
253225
254226 Expression :: TemplateLiteral ( lit) => {
255- // Only convert template literals with no expressions (pure strings)
256- if lit. expressions . is_empty ( ) && lit. quasis . len ( ) == 1 {
257- let raw = & lit. quasis [ 0 ] . value . cooked . as_ref ( ) ?;
258- Some ( serde_json:: Value :: String ( raw. to_string ( ) ) )
259- } else {
260- None
261- }
227+ let quasi = lit. single_quasi ( ) ?;
228+ Some ( serde_json:: Value :: String ( quasi. to_string ( ) ) )
262229 }
263230
264231 Expression :: UnaryExpression ( unary) => {
@@ -274,17 +241,13 @@ fn expr_to_json(expr: &Expression<'_>) -> Option<serde_json::Value> {
274241 Expression :: ArrayExpression ( arr) => {
275242 let mut values = Vec :: with_capacity ( arr. elements . len ( ) ) ;
276243 for elem in & arr. elements {
277- match elem {
278- ArrayExpressionElement :: Elision ( _) => {
279- values. push ( serde_json:: Value :: Null ) ;
280- }
281- ArrayExpressionElement :: SpreadElement ( _) => {
282- return None ;
283- }
284- _ => {
285- let elem_expr = elem. as_expression ( ) ?;
286- values. push ( expr_to_json ( elem_expr) ?) ;
287- }
244+ if elem. is_elision ( ) {
245+ values. push ( serde_json:: Value :: Null ) ;
246+ } else if elem. is_spread ( ) {
247+ return None ;
248+ } else {
249+ let elem_expr = elem. as_expression ( ) ?;
250+ values. push ( expr_to_json ( elem_expr) ?) ;
288251 }
289252 }
290253 Some ( serde_json:: Value :: Array ( values) )
@@ -293,19 +256,15 @@ fn expr_to_json(expr: &Expression<'_>) -> Option<serde_json::Value> {
293256 Expression :: ObjectExpression ( obj) => {
294257 let mut map = serde_json:: Map :: new ( ) ;
295258 for prop in & obj. properties {
296- match prop {
297- ObjectPropertyKind :: ObjectProperty ( prop) => {
298- if prop. computed {
299- return None ;
300- }
301- let key = property_key_to_json_key ( & prop. key ) ?;
302- let value = expr_to_json ( & prop. value ) ?;
303- map. insert ( key, value) ;
304- }
305- ObjectPropertyKind :: SpreadProperty ( _) => {
306- return None ;
307- }
259+ if prop. is_spread ( ) {
260+ return None ;
308261 }
262+ let ObjectPropertyKind :: ObjectProperty ( prop) = prop else {
263+ continue ;
264+ } ;
265+ let key = prop. key . static_name ( ) ?;
266+ let value = expr_to_json ( & prop. value ) ?;
267+ map. insert ( key. into_owned ( ) , value) ;
309268 }
310269 Some ( serde_json:: Value :: Object ( map) )
311270 }
@@ -314,26 +273,6 @@ fn expr_to_json(expr: &Expression<'_>) -> Option<serde_json::Value> {
314273 }
315274}
316275
317- /// Convert a property key to a JSON-compatible string key.
318- #[ expect( clippy:: disallowed_types) ]
319- fn property_key_to_json_key ( key : & PropertyKey < ' _ > ) -> Option < String > {
320- match key {
321- PropertyKey :: StaticIdentifier ( ident) => Some ( ident. name . to_string ( ) ) ,
322- PropertyKey :: StringLiteral ( lit) => Some ( lit. value . to_string ( ) ) ,
323- PropertyKey :: NumericLiteral ( lit) => {
324- if lit. value . fract ( ) == 0.0 && lit. value . is_finite ( ) {
325- #[ expect( clippy:: cast_possible_truncation) ]
326- {
327- Some ( ( lit. value as i64 ) . to_string ( ) )
328- }
329- } else {
330- Some ( lit. value . to_string ( ) )
331- }
332- }
333- _ => None ,
334- }
335- }
336-
337276#[ cfg( test) ]
338277mod tests {
339278 use tempfile:: TempDir ;
0 commit comments