@@ -8,23 +8,16 @@ use proc_macro2::TokenStream;
88use quote:: quote;
99use rustc_hash:: FxHashSet ;
1010
11- use oxc_allocator:: { Allocator , CloneIn } ;
11+ use oxc_allocator:: Allocator ;
1212use oxc_ast:: {
1313 AstBuilder , NONE ,
1414 ast:: {
15- Argument , BindingPatternKind , Expression , FormalParameterKind , FunctionType ,
16- LogicalOperator , ObjectExpression , ObjectPropertyKind , Program , PropertyKind , Statement ,
17- UnaryOperator ,
15+ Argument , Expression , FormalParameterKind , FunctionType , LogicalOperator , ObjectExpression ,
16+ ObjectPropertyKind , Program , PropertyKind ,
1817 } ,
1918} ;
20- use oxc_ast_visit:: { VisitMut , walk_mut} ;
21- use oxc_codegen:: Codegen as Printer ;
22- use oxc_minifier:: {
23- CompressOptions , CompressOptionsKeepNames , Minifier , MinifierOptions , PropertyReadSideEffects ,
24- TreeShakeOptions ,
25- } ;
26- use oxc_parser:: Parser ;
27- use oxc_span:: { SPAN , SourceType } ;
19+ use oxc_ast_visit:: VisitMut ;
20+ use oxc_span:: SPAN ;
2821
2922use crate :: {
3023 ALLOCATOR_CRATE_PATH , Generator , NAPI_PARSER_PACKAGE_PATH , OXLINT_APP_PATH ,
@@ -33,7 +26,7 @@ use crate::{
3326 get_fieldless_variant_value, get_struct_field_name, should_flatten_field,
3427 should_skip_enum_variant, should_skip_field,
3528 } ,
36- output:: Output ,
29+ output:: { Output , javascript :: VariantGenerator } ,
3730 schema:: {
3831 BoxDef , CellDef , Def , EnumDef , FieldDef , MetaType , OptionDef , PointerDef , PrimitiveDef ,
3932 Schema , StructDef , TypeDef , TypeId , VecDef ,
@@ -129,6 +122,7 @@ impl Generator for RawTransferGenerator {
129122///
130123/// When printing the JS and TS deserializers, the value of `IS_TS` is set to `true` or `false`,
131124/// and minifier then shakes out the dead code for each.
125+ #[ expect( clippy:: items_after_statements) ]
132126fn generate_deserializers (
133127 consts : Constants ,
134128 schema : & Schema ,
@@ -145,12 +139,6 @@ fn generate_deserializers(
145139 let mut code = format ! ( "
146140 let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen;
147141
148- const IS_TS = false;
149- const RANGE = false;
150- const LOC = false;
151- const PARENT = false;
152- const PRESERVE_PARENS = false;
153-
154142 const textDecoder = new TextDecoder('utf-8', {{ ignoreBOM: true }}),
155143 decodeStr = textDecoder.decode.bind(textDecoder),
156144 {{ fromCodePoint }} = String;
@@ -215,62 +203,64 @@ fn generate_deserializers(
215203 }
216204 }
217205
218- // Parse generated code
219- let allocator = Allocator :: new ( ) ;
220- let source_type = SourceType :: mjs ( ) ;
221- let parser_ret = Parser :: new ( & allocator, & code, source_type) . parse ( ) ;
222- assert ! ( parser_ret. errors. is_empty( ) , "Parse errors: {:#?}" , parser_ret. errors) ;
223- let program = parser_ret. program ;
224-
225- // Create deserializers with various settings, by setting `IS_TS`, `RANGE` and `PRESERVE_PARENS` consts,
226- // and running through minifier to shake out irrelevant code
227- let mut print_allocator = Allocator :: new ( ) ;
228- let mut deserializers = vec ! [ ] ;
229- let mut create_deserializer = |is_ts, range, loc, parent, preserve_parens| {
230- let mut program = program. clone_in ( & print_allocator) ;
231- replace_const ( & mut program, "IS_TS" , is_ts) ;
232- replace_const ( & mut program, "RANGE" , range) ;
233- replace_const ( & mut program, "LOC" , loc) ;
234- replace_const ( & mut program, "PARENT" , parent) ;
235- replace_const ( & mut program, "PRESERVE_PARENS" , preserve_parens) ;
236-
237- if loc {
238- assert ! ( range, "`loc` requires `range`" ) ;
239- LocFieldAdder :: new ( & allocator) . visit_program ( & mut program) ;
240- }
206+ // Create deserializers with various settings, by setting `IS_TS`, `RANGE`, `LOC`, `PARENT`
207+ // and `PRESERVE_PARENS` consts, and running through minifier to shake out irrelevant code
208+ struct VariantGen {
209+ variant_names : Vec < String > ,
210+ }
241211
242- let code = print_minified ( & mut program , & print_allocator ) ;
243- print_allocator . reset ( ) ;
212+ impl VariantGenerator < 5 > for VariantGen {
213+ const FLAG_NAMES : [ & str ; 5 ] = [ "IS_TS" , "RANGE" , "LOC" , "PARENT" , "PRESERVE_PARENS" ] ;
244214
245- let mut name = if is_ts { "ts" } else { "js" } . to_string ( ) ;
246- if range {
247- name. push_str ( "_range" ) ;
248- }
249- if loc {
250- name. push_str ( "_loc" ) ;
251- }
252- if parent {
253- name. push_str ( "_parent" ) ;
254- }
255- if !preserve_parens {
256- name. push_str ( "_no_parens" ) ;
257- }
215+ fn variants ( & mut self ) -> Vec < [ bool ; 5 ] > {
216+ let mut variants = Vec :: with_capacity ( 9 ) ;
258217
259- deserializers. push ( ( name, code) ) ;
260- } ;
218+ for is_ts in [ false , true ] {
219+ for range in [ false , true ] {
220+ for parent in [ false , true ] {
221+ let mut name = if is_ts { "ts" } else { "js" } . to_string ( ) ;
222+ if range {
223+ name. push_str ( "_range" ) ;
224+ }
225+ if parent {
226+ name. push_str ( "_parent" ) ;
227+ }
228+ self . variant_names . push ( name) ;
229+
230+ variants. push ( [
231+ is_ts, range, /* loc */ false , parent,
232+ /* preserve_parens */ true ,
233+ ] ) ;
234+ }
235+ }
236+ }
261237
262- for is_ts in [ false , true ] {
263- for range in [ false , true ] {
264- for parent in [ false , true ] {
265- create_deserializer ( is_ts, range, false , parent, true ) ;
238+ self . variant_names . push ( "ts_range_loc_parent_no_parens" . to_string ( ) ) ;
239+ variants. push ( [
240+ /* is_ts */ true , /* range */ true , /* loc */ true ,
241+ /* parent */ true , /* preserve_parens */ false ,
242+ ] ) ;
243+
244+ variants
245+ }
246+
247+ fn pre_process_variant < ' a > (
248+ & mut self ,
249+ program : & mut Program < ' a > ,
250+ flags : [ bool ; 5 ] ,
251+ allocator : & ' a Allocator ,
252+ ) {
253+ if flags[ 2 ] {
254+ // `loc` enabled
255+ LocFieldAdder :: new ( allocator) . visit_program ( program) ;
266256 }
267257 }
268258 }
269259
270- // `PRESERVE_PARENS = false` is only required for linter
271- create_deserializer ( true , true , true , true , false ) ;
260+ let mut generator = VariantGen { variant_names : vec ! [ ] } ;
261+ let codes = generator . generate ( & code ) ;
272262
273- deserializers
263+ generator . variant_names . into_iter ( ) . zip ( codes ) . collect ( )
274264}
275265
276266/// Type of deserializer in which some code appears.
@@ -1283,94 +1273,6 @@ fn get_constants(schema: &Schema) -> Constants {
12831273 }
12841274}
12851275
1286- /// Replace the value of a `const` declaration with `true` / `false`.
1287- ///
1288- /// Only replaces `const`s defined at top level which are currently defined as a boolean.
1289- fn replace_const ( program : & mut Program < ' _ > , const_name : & str , value : bool ) {
1290- for stmt in & mut program. body {
1291- let Statement :: VariableDeclaration ( var_decl) = stmt else { continue } ;
1292- if !var_decl. kind . is_const ( ) {
1293- continue ;
1294- }
1295-
1296- for declarator in & mut var_decl. declarations {
1297- if let BindingPatternKind :: BindingIdentifier ( ident) = & declarator. id . kind
1298- && ident. name == const_name
1299- {
1300- let init = declarator. init . as_mut ( ) . unwrap ( ) ;
1301- let Expression :: BooleanLiteral ( bool_lit) = init else { continue } ;
1302- bool_lit. value = value;
1303- return ;
1304- }
1305- }
1306- }
1307-
1308- panic ! ( "`{const_name}` const not found" ) ;
1309- }
1310-
1311- /// Print AST with minified syntax.
1312- ///
1313- /// Do not remove whitespace, or mangle symbols.
1314- /// Purpose is not to compress length of code, but to remove dead code.
1315- fn print_minified < ' a > ( program : & mut Program < ' a > , allocator : & ' a Allocator ) -> String {
1316- // Minify
1317- let minify_options = MinifierOptions {
1318- mangle : None ,
1319- compress : Some ( CompressOptions {
1320- keep_names : CompressOptionsKeepNames :: all_true ( ) ,
1321- sequences : false ,
1322- treeshake : TreeShakeOptions {
1323- property_read_side_effects : PropertyReadSideEffects :: None ,
1324- ..TreeShakeOptions :: default ( )
1325- } ,
1326- ..CompressOptions :: default ( )
1327- } ) ,
1328- } ;
1329- Minifier :: new ( minify_options) . minify ( allocator, program) ;
1330-
1331- // Revert minification of `true` to `!0` and `false` to `!1`. It hurts readability.
1332- let mut unminifier = BooleanUnminifier :: new ( allocator) ;
1333- unminifier. visit_program ( program) ;
1334-
1335- // Print. Add back line breaks between functions to aid readability.
1336- let mut code = Printer :: new ( ) . build ( program) . code ;
1337-
1338- #[ expect( clippy:: items_after_statements) ]
1339- static RE : Lazy < Regex > = lazy_regex ! ( r"\n(function|export) " ) ;
1340- code = RE
1341- . replace_all ( & code, |caps : & Captures | {
1342- // `format!("\n\n{} ", &caps[1])` would be simpler, but this avoids allocations
1343- if & caps[ 1 ] == "function" { "\n \n function " } else { "\n \n export " }
1344- } )
1345- . into_owned ( ) ;
1346-
1347- code
1348- }
1349-
1350- /// Visitor which converts `!0` to `true` and `!1` to `false`.
1351- struct BooleanUnminifier < ' a > {
1352- ast : AstBuilder < ' a > ,
1353- }
1354-
1355- impl < ' a > BooleanUnminifier < ' a > {
1356- fn new ( allocator : & ' a Allocator ) -> Self {
1357- Self { ast : AstBuilder :: new ( allocator) }
1358- }
1359- }
1360-
1361- impl < ' a > VisitMut < ' a > for BooleanUnminifier < ' a > {
1362- fn visit_expression ( & mut self , expr : & mut Expression < ' a > ) {
1363- if let Expression :: UnaryExpression ( unary_expr) = expr
1364- && unary_expr. operator == UnaryOperator :: LogicalNot
1365- && let Expression :: NumericLiteral ( lit) = & unary_expr. argument
1366- {
1367- * expr = self . ast . expression_boolean_literal ( unary_expr. span , lit. value == 0.0 ) ;
1368- return ;
1369- }
1370- walk_mut:: walk_expression ( self , expr) ;
1371- }
1372- }
1373-
13741276/// Visitor to add `loc` field after `range` in all deserialize functions.
13751277///
13761278/// Works on AST pre-minification.
0 commit comments