Skip to main content

node_shim/
lib.rs

1// Copyright 2018-2026 the Deno authors. MIT license.
2
3/*!
4 * Node.js CLI Argument Parser - Rust Implementation
5 *
6 * This is a Rust implementation that matches the exact behavior of
7 * Node.js CLI argument parsing found in src/node_options.cc and related files.
8 *
9 * Based on Node.js source code analysis of:
10 * - src/node_options.cc (option definitions and parsing logic)
11 * - src/node_options.h (option structures and types)
12 * - src/node_options-inl.h (template implementation)
13 * - src/node.cc (main execution flow)
14 */
15
16use std::collections::HashMap;
17
18#[derive(Debug, Clone, PartialEq)]
19pub enum OptionType {
20  NoOp,
21  V8Option,
22  Boolean,
23  Integer,
24  UInteger,
25  String,
26  HostPort,
27  StringList,
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub enum OptionEnvvarSettings {
32  DisallowedInEnvvar,
33  AllowedInEnvvar,
34}
35
36#[derive(Debug, Clone)]
37pub struct HostPort {
38  pub host: String,
39  pub port: u16,
40}
41
42impl Default for HostPort {
43  fn default() -> Self {
44    Self {
45      host: "127.0.0.1".to_string(),
46      port: 9229,
47    }
48  }
49}
50
51impl HostPort {
52  pub fn new(host: String, port: u16) -> Self {
53    Self { host, port }
54  }
55
56  pub fn update(&mut self, other: &HostPort) {
57    if !other.host.is_empty() {
58      self.host = other.host.clone();
59    }
60    self.port = other.port;
61  }
62}
63
64#[derive(Debug, Clone, Default)]
65pub struct InspectPublishUid {
66  pub console: bool,
67  pub http: bool,
68}
69
70#[derive(Debug, Clone)]
71pub struct DebugOptions {
72  pub allow_attaching_debugger: bool,
73  pub inspector_enabled: bool,
74  pub inspect_wait: bool,
75  pub deprecated_debug: bool,
76  pub break_first_line: bool,
77  pub break_node_first_line: bool,
78  pub inspect_publish_uid_string: String,
79  pub inspect_publish_uid: InspectPublishUid,
80  pub host_port: HostPort,
81}
82
83impl Default for DebugOptions {
84  fn default() -> Self {
85    Self {
86      allow_attaching_debugger: true,
87      inspector_enabled: false,
88      inspect_wait: false,
89      deprecated_debug: false,
90      break_first_line: false,
91      break_node_first_line: false,
92      inspect_publish_uid_string: "stderr,http".to_string(),
93      inspect_publish_uid: InspectPublishUid::default(),
94      host_port: HostPort::default(),
95    }
96  }
97}
98
99impl DebugOptions {
100  pub fn enable_break_first_line(&mut self) {
101    self.inspector_enabled = true;
102    self.break_first_line = true;
103  }
104
105  pub fn disable_wait_or_break_first_line(&mut self) {
106    self.inspect_wait = false;
107    self.break_first_line = false;
108  }
109
110  pub fn wait_for_connect(&self) -> bool {
111    self.break_first_line || self.break_node_first_line || self.inspect_wait
112  }
113
114  pub fn should_break_first_line(&self) -> bool {
115    self.break_first_line || self.break_node_first_line
116  }
117
118  pub fn check_options(&mut self, errors: &mut Vec<String>) {
119    let entries: Vec<&str> =
120      self.inspect_publish_uid_string.split(',').collect();
121    self.inspect_publish_uid.console = false;
122    self.inspect_publish_uid.http = false;
123
124    for entry in entries {
125      let destination = entry.trim();
126      match destination {
127        "stderr" => self.inspect_publish_uid.console = true,
128        "http" => self.inspect_publish_uid.http = true,
129        _ => errors.push(
130          "--inspect-publish-uid destination can be stderr or http".to_string(),
131        ),
132      }
133    }
134
135    if self.deprecated_debug {
136      errors.push("[DEP0062]: `node --debug` and `node --debug-brk` are invalid. Please use `node --inspect` and `node --inspect-brk` instead.".to_string());
137    }
138  }
139}
140
141#[derive(Debug, Clone)]
142pub struct EnvironmentOptions {
143  pub abort_on_uncaught_exception: bool,
144  pub conditions: Vec<String>,
145  pub detect_module: bool,
146  pub disable_sigusr1: bool,
147  pub print_required_tla: bool,
148  pub require_module: bool,
149  pub dns_result_order: String,
150  pub enable_source_maps: bool,
151  pub experimental_addon_modules: bool,
152  pub experimental_eventsource: bool,
153  pub experimental_fetch: bool,
154  pub experimental_websocket: bool,
155  pub experimental_sqlite: bool,
156  pub experimental_webstorage: bool,
157  pub experimental_quic: bool,
158  pub localstorage_file: String,
159  pub experimental_global_navigator: bool,
160  pub experimental_global_web_crypto: bool,
161  pub experimental_wasm_modules: bool,
162  pub experimental_import_meta_resolve: bool,
163  pub input_type: String,
164  pub entry_is_url: bool,
165  pub permission: bool,
166  pub allow_fs_read: Vec<String>,
167  pub allow_fs_write: Vec<String>,
168  pub allow_addons: bool,
169  pub allow_child_process: bool,
170  pub allow_net: Vec<String>,
171  pub allow_wasi: bool,
172  pub allow_worker_threads: bool,
173  pub experimental_repl_await: bool,
174  pub experimental_vm_modules: bool,
175  pub async_context_frame: bool,
176  pub expose_internals: bool,
177  pub force_node_api_uncaught_exceptions_policy: bool,
178  pub frozen_intrinsics: bool,
179  pub heap_snapshot_near_heap_limit: i64,
180  pub heapsnapshot_signal: String,
181  pub network_family_autoselection: bool,
182  pub network_family_autoselection_attempt_timeout: u64,
183  pub max_http_header_size: u64,
184  pub deprecation: bool,
185  pub force_async_hooks_checks: bool,
186  pub allow_native_addons: bool,
187  pub global_search_paths: bool,
188  pub warnings: bool,
189  pub disable_warnings: Vec<String>,
190  pub force_context_aware: bool,
191  pub pending_deprecation: bool,
192  pub preserve_symlinks: bool,
193  pub preserve_symlinks_main: bool,
194  pub prof_process: bool,
195  pub cpu_prof_dir: String,
196  pub cpu_prof_interval: u64,
197  pub cpu_prof_name: String,
198  pub cpu_prof: bool,
199  pub experimental_network_inspection: bool,
200  pub experimental_worker_inspection: bool,
201  pub heap_prof_dir: String,
202  pub heap_prof_name: String,
203  pub heap_prof_interval: u64,
204  pub heap_prof: bool,
205  pub redirect_warnings: String,
206  pub diagnostic_dir: String,
207  pub env_file: String,
208  pub optional_env_file: String,
209  pub has_env_file_string: bool,
210  pub test_runner: bool,
211  pub test_runner_concurrency: u64,
212  pub test_runner_timeout: u64,
213  pub test_runner_coverage: bool,
214  pub test_runner_force_exit: bool,
215  pub test_coverage_branches: u64,
216  pub test_coverage_functions: u64,
217  pub test_coverage_lines: u64,
218  pub test_runner_module_mocks: bool,
219  pub test_runner_update_snapshots: bool,
220  pub test_name_pattern: Vec<String>,
221  pub test_reporter: Vec<String>,
222  pub test_reporter_destination: Vec<String>,
223  pub test_global_setup_path: String,
224  pub test_only: bool,
225  pub test_udp_no_try_send: bool,
226  pub test_isolation: String,
227  pub test_shard: String,
228  pub test_skip_pattern: Vec<String>,
229  pub coverage_include_pattern: Vec<String>,
230  pub coverage_exclude_pattern: Vec<String>,
231  pub throw_deprecation: bool,
232  pub trace_deprecation: bool,
233  pub trace_exit: bool,
234  pub trace_sync_io: bool,
235  pub trace_tls: bool,
236  pub trace_uncaught: bool,
237  pub trace_warnings: bool,
238  pub trace_promises: bool,
239  pub trace_env: bool,
240  pub trace_env_js_stack: bool,
241  pub trace_env_native_stack: bool,
242  pub trace_require_module: String,
243  pub extra_info_on_fatal_exception: bool,
244  pub unhandled_rejections: String,
245  pub userland_loaders: Vec<String>,
246  pub verify_base_objects: bool,
247  pub watch_mode: bool,
248  pub watch_mode_report_to_parent: bool,
249  pub watch_mode_preserve_output: bool,
250  pub watch_mode_kill_signal: String,
251  pub watch_mode_paths: Vec<String>,
252  pub syntax_check_only: bool,
253  pub has_eval_string: bool,
254  pub eval_string: String,
255  pub print_eval: bool,
256  pub force_repl: bool,
257  pub insecure_http_parser: bool,
258  pub tls_min_v1_0: bool,
259  pub tls_min_v1_1: bool,
260  pub tls_min_v1_2: bool,
261  pub tls_min_v1_3: bool,
262  pub tls_max_v1_2: bool,
263  pub tls_max_v1_3: bool,
264  pub tls_keylog: String,
265  pub preload_cjs_modules: Vec<String>,
266  pub preload_esm_modules: Vec<String>,
267  pub experimental_strip_types: bool,
268  pub experimental_transform_types: bool,
269  pub user_argv: Vec<String>,
270  pub report_exclude_env: bool,
271  pub report_exclude_network: bool,
272  pub experimental_config_file_path: String,
273  pub experimental_default_config_file: bool,
274  pub debug_options: DebugOptions,
275}
276
277impl Default for EnvironmentOptions {
278  fn default() -> Self {
279    Self {
280      abort_on_uncaught_exception: false,
281      conditions: Vec::new(),
282      detect_module: true,
283      disable_sigusr1: false,
284      print_required_tla: false,
285      require_module: true,
286      dns_result_order: String::new(),
287      enable_source_maps: false,
288      experimental_addon_modules: false,
289      experimental_eventsource: false,
290      experimental_fetch: true,
291      experimental_websocket: true,
292      experimental_sqlite: true,
293      experimental_webstorage: false,
294      experimental_quic: false,
295      localstorage_file: String::new(),
296      experimental_global_navigator: true,
297      experimental_global_web_crypto: true,
298      experimental_wasm_modules: false,
299      experimental_import_meta_resolve: false,
300      input_type: String::new(),
301      entry_is_url: false,
302      permission: false,
303      allow_fs_read: Vec::new(),
304      allow_fs_write: Vec::new(),
305      allow_addons: false,
306      allow_child_process: false,
307      allow_net: Vec::new(),
308      allow_wasi: false,
309      allow_worker_threads: false,
310      experimental_repl_await: true,
311      experimental_vm_modules: false,
312      async_context_frame: true,
313      expose_internals: false,
314      force_node_api_uncaught_exceptions_policy: false,
315      frozen_intrinsics: false,
316      heap_snapshot_near_heap_limit: 0,
317      heapsnapshot_signal: String::new(),
318      network_family_autoselection: true,
319      network_family_autoselection_attempt_timeout: 250,
320      max_http_header_size: 16 * 1024,
321      deprecation: true,
322      force_async_hooks_checks: true,
323      allow_native_addons: true,
324      global_search_paths: true,
325      warnings: true,
326      disable_warnings: Vec::new(),
327      force_context_aware: false,
328      pending_deprecation: false,
329      preserve_symlinks: false,
330      preserve_symlinks_main: false,
331      prof_process: false,
332      cpu_prof_dir: String::new(),
333      cpu_prof_interval: 1000,
334      cpu_prof_name: String::new(),
335      cpu_prof: false,
336      experimental_network_inspection: false,
337      experimental_worker_inspection: false,
338      heap_prof_dir: String::new(),
339      heap_prof_name: String::new(),
340      heap_prof_interval: 512 * 1024,
341      heap_prof: false,
342      redirect_warnings: String::new(),
343      diagnostic_dir: String::new(),
344      env_file: String::new(),
345      optional_env_file: String::new(),
346      has_env_file_string: false,
347      test_runner: false,
348      test_runner_concurrency: 0,
349      test_runner_timeout: 0,
350      test_runner_coverage: false,
351      test_runner_force_exit: false,
352      test_coverage_branches: 0,
353      test_coverage_functions: 0,
354      test_coverage_lines: 0,
355      test_runner_module_mocks: false,
356      test_runner_update_snapshots: false,
357      test_name_pattern: Vec::new(),
358      test_reporter: Vec::new(),
359      test_reporter_destination: Vec::new(),
360      test_global_setup_path: String::new(),
361      test_only: false,
362      test_udp_no_try_send: false,
363      test_isolation: "process".to_string(),
364      test_shard: String::new(),
365      test_skip_pattern: Vec::new(),
366      coverage_include_pattern: Vec::new(),
367      coverage_exclude_pattern: Vec::new(),
368      throw_deprecation: false,
369      trace_deprecation: false,
370      trace_exit: false,
371      trace_sync_io: false,
372      trace_tls: false,
373      trace_uncaught: false,
374      trace_warnings: false,
375      trace_promises: false,
376      trace_env: false,
377      trace_env_js_stack: false,
378      trace_env_native_stack: false,
379      trace_require_module: String::new(),
380      extra_info_on_fatal_exception: true,
381      unhandled_rejections: String::new(),
382      userland_loaders: Vec::new(),
383      verify_base_objects: false,
384      watch_mode: false,
385      watch_mode_report_to_parent: false,
386      watch_mode_preserve_output: false,
387      watch_mode_kill_signal: "SIGTERM".to_string(),
388      watch_mode_paths: Vec::new(),
389      syntax_check_only: false,
390      has_eval_string: false,
391      eval_string: String::new(),
392      print_eval: false,
393      force_repl: false,
394      insecure_http_parser: false,
395      tls_min_v1_0: false,
396      tls_min_v1_1: false,
397      tls_min_v1_2: false,
398      tls_min_v1_3: false,
399      tls_max_v1_2: false,
400      tls_max_v1_3: false,
401      tls_keylog: String::new(),
402      preload_cjs_modules: Vec::new(),
403      preload_esm_modules: Vec::new(),
404      experimental_strip_types: true,
405      experimental_transform_types: false,
406      user_argv: Vec::new(),
407      report_exclude_env: false,
408      report_exclude_network: false,
409      experimental_config_file_path: String::new(),
410      experimental_default_config_file: false,
411      debug_options: DebugOptions::default(),
412    }
413  }
414}
415
416impl EnvironmentOptions {
417  pub fn check_options(&mut self, errors: &mut Vec<String>) {
418    if !self.input_type.is_empty()
419      && !matches!(
420        self.input_type.as_str(),
421        "commonjs" | "module" | "commonjs-typescript" | "module-typescript"
422      )
423    {
424      errors.push("--input-type must be \"module\", \"commonjs\", \"module-typescript\" or \"commonjs-typescript\"".to_string());
425    }
426
427    if self.syntax_check_only && self.has_eval_string {
428      errors.push("either --check or --eval can be used, not both".to_string());
429    }
430
431    if !self.unhandled_rejections.is_empty()
432      && !matches!(
433        self.unhandled_rejections.as_str(),
434        "warn-with-error-code" | "throw" | "strict" | "warn" | "none"
435      )
436    {
437      errors.push("invalid value for --unhandled-rejections".to_string());
438    }
439
440    if self.tls_min_v1_3 && self.tls_max_v1_2 {
441      errors.push(
442        "either --tls-min-v1.3 or --tls-max-v1.2 can be used, not both"
443          .to_string(),
444      );
445    }
446
447    if self.heap_snapshot_near_heap_limit < 0 {
448      errors.push(
449        "--heapsnapshot-near-heap-limit must not be negative".to_string(),
450      );
451    }
452
453    if !self.trace_require_module.is_empty()
454      && !matches!(
455        self.trace_require_module.as_str(),
456        "all" | "no-node-modules"
457      )
458    {
459      errors.push("invalid value for --trace-require-module".to_string());
460    }
461
462    if self.test_runner {
463      if self.test_isolation == "none" {
464        self.debug_options.allow_attaching_debugger = true;
465      } else if self.test_isolation != "process" {
466        errors.push("invalid value for --test-isolation".to_string());
467      }
468
469      if self.syntax_check_only {
470        errors
471          .push("either --test or --check can be used, not both".to_string());
472      }
473
474      if self.has_eval_string {
475        errors
476          .push("either --test or --eval can be used, not both".to_string());
477      }
478
479      if self.force_repl {
480        errors.push(
481          "either --test or --interactive can be used, not both".to_string(),
482        );
483      }
484
485      if !self.watch_mode_paths.is_empty() {
486        errors.push(
487          "--watch-path cannot be used in combination with --test".to_string(),
488        );
489      }
490    }
491
492    if self.watch_mode {
493      if self.syntax_check_only {
494        errors
495          .push("either --watch or --check can be used, not both".to_string());
496      } else if self.has_eval_string {
497        errors
498          .push("either --watch or --eval can be used, not both".to_string());
499      } else if self.force_repl {
500        errors.push(
501          "either --watch or --interactive can be used, not both".to_string(),
502        );
503      } else if self.test_runner_force_exit {
504        errors.push(
505          "either --watch or --test-force-exit can be used, not both"
506            .to_string(),
507        );
508      }
509
510      self.debug_options.allow_attaching_debugger = false;
511    }
512
513    // CPU profiling validation
514    if !self.cpu_prof {
515      if !self.cpu_prof_name.is_empty() {
516        errors.push("--cpu-prof-name must be used with --cpu-prof".to_string());
517      }
518      if !self.cpu_prof_dir.is_empty() {
519        errors.push("--cpu-prof-dir must be used with --cpu-prof".to_string());
520      }
521      if self.cpu_prof_interval != 1000 {
522        errors
523          .push("--cpu-prof-interval must be used with --cpu-prof".to_string());
524      }
525    }
526
527    if self.cpu_prof
528      && self.cpu_prof_dir.is_empty()
529      && !self.diagnostic_dir.is_empty()
530    {
531      self.cpu_prof_dir = self.diagnostic_dir.clone();
532    }
533
534    // Heap profiling validation
535    if !self.heap_prof {
536      if !self.heap_prof_name.is_empty() {
537        errors
538          .push("--heap-prof-name must be used with --heap-prof".to_string());
539      }
540      if !self.heap_prof_dir.is_empty() {
541        errors
542          .push("--heap-prof-dir must be used with --heap-prof".to_string());
543      }
544      if self.heap_prof_interval != 512 * 1024 {
545        errors.push(
546          "--heap-prof-interval must be used with --heap-prof".to_string(),
547        );
548      }
549    }
550
551    if self.heap_prof
552      && self.heap_prof_dir.is_empty()
553      && !self.diagnostic_dir.is_empty()
554    {
555      self.heap_prof_dir = self.diagnostic_dir.clone();
556    }
557
558    self.debug_options.check_options(errors);
559  }
560}
561
562#[derive(Debug, Clone)]
563pub struct PerIsolateOptions {
564  pub per_env: EnvironmentOptions,
565  pub track_heap_objects: bool,
566  pub report_uncaught_exception: bool,
567  pub report_on_signal: bool,
568  pub experimental_shadow_realm: bool,
569  pub stack_trace_limit: i64,
570  pub report_signal: String,
571  pub build_snapshot: bool,
572  pub build_snapshot_config: String,
573}
574
575impl Default for PerIsolateOptions {
576  fn default() -> Self {
577    Self {
578      per_env: EnvironmentOptions::default(),
579      track_heap_objects: false,
580      report_uncaught_exception: false,
581      report_on_signal: false,
582      experimental_shadow_realm: false,
583      stack_trace_limit: 10,
584      report_signal: "SIGUSR2".to_string(),
585      build_snapshot: false,
586      build_snapshot_config: String::new(),
587    }
588  }
589}
590
591impl PerIsolateOptions {
592  pub fn check_options(&mut self, errors: &mut Vec<String>) {
593    self.per_env.check_options(errors);
594  }
595}
596
597#[derive(Debug, Clone)]
598pub struct PerProcessOptions {
599  pub per_isolate: PerIsolateOptions,
600  pub title: String,
601  pub trace_event_categories: String,
602  pub trace_event_file_pattern: String,
603  pub v8_thread_pool_size: i64,
604  pub zero_fill_all_buffers: bool,
605  pub debug_arraybuffer_allocations: bool,
606  pub disable_proto: String,
607  pub node_snapshot: bool,
608  pub snapshot_blob: String,
609  pub security_reverts: Vec<String>,
610  pub print_bash_completion: bool,
611  pub print_help: bool,
612  pub print_v8_help: bool,
613  pub print_version: bool,
614  pub experimental_sea_config: String,
615  pub run: String,
616  pub icu_data_dir: String,
617  pub openssl_config: String,
618  pub tls_cipher_list: String,
619  pub secure_heap: i64,
620  pub secure_heap_min: i64,
621  pub ssl_openssl_cert_store: bool,
622  pub use_openssl_ca: bool,
623  pub use_system_ca: bool,
624  pub use_bundled_ca: bool,
625  pub enable_fips_crypto: bool,
626  pub force_fips_crypto: bool,
627  pub openssl_legacy_provider: bool,
628  pub openssl_shared_config: bool,
629  pub disable_wasm_trap_handler: bool,
630  pub report_on_fatalerror: bool,
631  pub report_compact: bool,
632  pub report_directory: String,
633  pub report_filename: String,
634  pub use_largepages: String,
635  pub trace_sigint: bool,
636}
637
638impl Default for PerProcessOptions {
639  fn default() -> Self {
640    Self {
641      per_isolate: PerIsolateOptions::default(),
642      title: String::new(),
643      trace_event_categories: String::new(),
644      trace_event_file_pattern: "node_trace.${rotation}.log".to_string(),
645      v8_thread_pool_size: 4,
646      zero_fill_all_buffers: false,
647      debug_arraybuffer_allocations: false,
648      disable_proto: String::new(),
649      node_snapshot: true,
650      snapshot_blob: String::new(),
651      security_reverts: Vec::new(),
652      print_bash_completion: false,
653      print_help: false,
654      print_v8_help: false,
655      print_version: false,
656      experimental_sea_config: String::new(),
657      run: String::new(),
658      icu_data_dir: String::new(),
659      openssl_config: String::new(),
660      tls_cipher_list:
661        "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS"
662          .to_string(),
663      secure_heap: 0,
664      secure_heap_min: 2,
665      ssl_openssl_cert_store: false,
666      use_openssl_ca: false,
667      use_system_ca: false,
668      use_bundled_ca: false,
669      enable_fips_crypto: false,
670      force_fips_crypto: false,
671      openssl_legacy_provider: false,
672      openssl_shared_config: false,
673      disable_wasm_trap_handler: false,
674      report_on_fatalerror: false,
675      report_compact: false,
676      report_directory: String::new(),
677      report_filename: String::new(),
678      use_largepages: "off".to_string(),
679      trace_sigint: false,
680    }
681  }
682}
683
684impl PerProcessOptions {
685  pub fn check_options(&mut self, errors: &mut Vec<String>) {
686    if self.use_openssl_ca && self.use_bundled_ca {
687      errors.push(
688        "either --use-openssl-ca or --use-bundled-ca can be used, not both"
689          .to_string(),
690      );
691    }
692
693    if self.secure_heap >= 2 {
694      if (self.secure_heap & (self.secure_heap - 1)) != 0 {
695        errors.push("--secure-heap must be a power of 2".to_string());
696      }
697      self.secure_heap_min = self
698        .secure_heap
699        .min(self.secure_heap_min)
700        .min(i32::MAX as i64);
701      self.secure_heap_min = self.secure_heap_min.max(2);
702      if (self.secure_heap_min & (self.secure_heap_min - 1)) != 0 {
703        errors.push("--secure-heap-min must be a power of 2".to_string());
704      }
705    }
706
707    if !matches!(self.use_largepages.as_str(), "off" | "on" | "silent") {
708      errors.push("invalid value for --use-largepages".to_string());
709    }
710
711    self.per_isolate.check_options(errors);
712  }
713}
714
715fn remove_brackets(host: &str) -> String {
716  if !host.is_empty() && host.starts_with('[') && host.ends_with(']') {
717    host[1..host.len() - 1].to_string()
718  } else {
719    host.to_string()
720  }
721}
722
723fn parse_and_validate_port(port_str: &str, errors: &mut Vec<String>) -> u16 {
724  match port_str.parse::<u16>() {
725    Ok(port) => {
726      if port != 0 && port < 1024 {
727        errors.push("must be 0 or in range 1024 to 65535.".to_string());
728        0
729      } else {
730        port
731      }
732    }
733    Err(_) => {
734      errors.push("must be 0 or in range 1024 to 65535.".to_string());
735      0
736    }
737  }
738}
739
740fn split_host_port(arg: &str, errors: &mut Vec<String>) -> HostPort {
741  let host = remove_brackets(arg);
742  if host.len() < arg.len() {
743    return HostPort::new(host, 9229);
744  }
745
746  if let Some(colon_index) = arg.rfind(':') {
747    let host_part = remove_brackets(&arg[..colon_index]);
748    let port_part = &arg[colon_index + 1..];
749    HostPort::new(host_part, parse_and_validate_port(port_part, errors))
750  } else {
751    // Either a port number or a host name
752    if arg.chars().all(|c| c.is_ascii_digit()) {
753      HostPort::new(String::new(), parse_and_validate_port(arg, errors))
754    } else {
755      HostPort::new(arg.to_string(), 9229)
756    }
757  }
758}
759
760#[derive(Debug, Clone)]
761#[allow(dead_code)]
762struct OptionInfo {
763  option_type: OptionType,
764  env_setting: OptionEnvvarSettings,
765  help_text: String,
766  default_is_true: bool,
767}
768
769pub struct OptionsParser {
770  options: HashMap<String, OptionInfo>,
771  aliases: HashMap<String, Vec<String>>,
772  implications: HashMap<String, Vec<String>>,
773}
774
775impl Default for OptionsParser {
776  fn default() -> Self {
777    Self::new()
778  }
779}
780
781impl OptionsParser {
782  pub fn new() -> Self {
783    let mut parser = Self {
784      options: HashMap::new(),
785      aliases: HashMap::new(),
786      implications: HashMap::new(),
787    };
788    parser.setup_options();
789    parser
790  }
791
792  fn add_option(
793    &mut self,
794    name: &str,
795    help_text: &str,
796    option_type: OptionType,
797    env_setting: OptionEnvvarSettings,
798    default_is_true: bool,
799  ) {
800    self.options.insert(
801      name.to_string(),
802      OptionInfo {
803        option_type,
804        env_setting,
805        help_text: help_text.to_string(),
806        default_is_true,
807      },
808    );
809  }
810
811  fn add_alias(&mut self, from: &str, to: Vec<&str>) {
812    self
813      .aliases
814      .insert(from.to_string(), to.iter().map(|s| s.to_string()).collect());
815  }
816
817  fn add_implication(&mut self, from: &str, to: Vec<&str>) {
818    self
819      .implications
820      .insert(from.to_string(), to.iter().map(|s| s.to_string()).collect());
821  }
822
823  fn setup_options(&mut self) {
824    // Debug options
825    self.add_option(
826      "--inspect-port",
827      "set host:port for inspector",
828      OptionType::HostPort,
829      OptionEnvvarSettings::AllowedInEnvvar,
830      false,
831    );
832    self.add_option(
833      "--inspect",
834      "activate inspector on host:port (default: 127.0.0.1:9229)",
835      OptionType::Boolean,
836      OptionEnvvarSettings::AllowedInEnvvar,
837      false,
838    );
839    self.add_option(
840      "--debug",
841      "",
842      OptionType::Boolean,
843      OptionEnvvarSettings::DisallowedInEnvvar,
844      false,
845    );
846    self.add_option(
847      "--debug-brk",
848      "",
849      OptionType::Boolean,
850      OptionEnvvarSettings::DisallowedInEnvvar,
851      false,
852    );
853    self.add_option(
854      "--inspect-brk",
855      "activate inspector on host:port and break at start of user script",
856      OptionType::Boolean,
857      OptionEnvvarSettings::AllowedInEnvvar,
858      false,
859    );
860    self.add_option(
861      "--inspect-brk-node",
862      "",
863      OptionType::Boolean,
864      OptionEnvvarSettings::DisallowedInEnvvar,
865      false,
866    );
867    self.add_option(
868      "--inspect-wait",
869      "activate inspector on host:port and wait for debugger to be attached",
870      OptionType::Boolean,
871      OptionEnvvarSettings::AllowedInEnvvar,
872      false,
873    );
874    self.add_option(
875            "--inspect-publish-uid",
876            "comma separated list of destinations for inspector uid (default: stderr,http)",
877            OptionType::String,
878            OptionEnvvarSettings::AllowedInEnvvar,
879            false,
880        );
881
882    // Environment options
883    self.add_option(
884      "--conditions",
885      "additional user conditions for conditional exports and imports",
886      OptionType::StringList,
887      OptionEnvvarSettings::AllowedInEnvvar,
888      false,
889    );
890    self.add_option("--experimental-detect-module", "when ambiguous modules fail to evaluate because they contain ES module syntax, try again to evaluate them as ES modules", OptionType::Boolean, OptionEnvvarSettings::AllowedInEnvvar, true);
891    self.add_option("--experimental-print-required-tla", "Print pending top-level await. If --experimental-require-module is true, evaluate asynchronous graphs loaded by `require()` but do not run the microtasks, in order to to find and print top-level await in the graph", OptionType::Boolean, OptionEnvvarSettings::AllowedInEnvvar, false);
892    self.add_option(
893      "--experimental-require-module",
894      "Allow loading synchronous ES Modules in require().",
895      OptionType::Boolean,
896      OptionEnvvarSettings::AllowedInEnvvar,
897      true,
898    );
899    self.add_option(
900      "--diagnostic-dir",
901      "set dir for all output files (default: current working directory)",
902      OptionType::String,
903      OptionEnvvarSettings::AllowedInEnvvar,
904      false,
905    );
906    self.add_option(
907      "--disable-sigusr1",
908      "Disable inspector thread to be listening for SIGUSR1 signal",
909      OptionType::Boolean,
910      OptionEnvvarSettings::AllowedInEnvvar,
911      false,
912    );
913    self.add_option("--dns-result-order", "set default value of verbatim in dns.lookup. Options are 'ipv4first' (IPv4 addresses are placed before IPv6 addresses) 'ipv6first' (IPv6 addresses are placed before IPv4 addresses) 'verbatim' (addresses are in the order the DNS resolver returned)", OptionType::String, OptionEnvvarSettings::AllowedInEnvvar, false);
914    self.add_option(
915      "--network-family-autoselection",
916      "Disable network address family autodetection algorithm",
917      OptionType::Boolean,
918      OptionEnvvarSettings::AllowedInEnvvar,
919      true,
920    );
921    self.add_option(
922            "--network-family-autoselection-attempt-timeout",
923            "Sets the default value for the network family autoselection attempt timeout.",
924            OptionType::UInteger,
925            OptionEnvvarSettings::AllowedInEnvvar,
926            false,
927        );
928    self.add_option(
929      "--enable-source-maps",
930      "Source Map V3 support for stack traces",
931      OptionType::Boolean,
932      OptionEnvvarSettings::AllowedInEnvvar,
933      false,
934    );
935    self.add_option(
936      "--entry-url",
937      "Treat the entrypoint as a URL",
938      OptionType::Boolean,
939      OptionEnvvarSettings::AllowedInEnvvar,
940      false,
941    );
942    self.add_option(
943      "--experimental-addon-modules",
944      "experimental import support for addons",
945      OptionType::Boolean,
946      OptionEnvvarSettings::AllowedInEnvvar,
947      false,
948    );
949    self.add_option(
950      "--experimental-abortcontroller",
951      "",
952      OptionType::NoOp,
953      OptionEnvvarSettings::AllowedInEnvvar,
954      false,
955    );
956    self.add_option(
957      "--experimental-eventsource",
958      "experimental EventSource API",
959      OptionType::Boolean,
960      OptionEnvvarSettings::AllowedInEnvvar,
961      false,
962    );
963    self.add_option(
964      "--experimental-fetch",
965      "",
966      OptionType::NoOp,
967      OptionEnvvarSettings::AllowedInEnvvar,
968      false,
969    );
970    self.add_option(
971      "--experimental-websocket",
972      "experimental WebSocket API",
973      OptionType::Boolean,
974      OptionEnvvarSettings::AllowedInEnvvar,
975      true,
976    );
977    self.add_option(
978      "--experimental-global-customevent",
979      "",
980      OptionType::NoOp,
981      OptionEnvvarSettings::AllowedInEnvvar,
982      false,
983    );
984    self.add_option(
985      "--experimental-sqlite",
986      "experimental node:sqlite module",
987      OptionType::Boolean,
988      OptionEnvvarSettings::AllowedInEnvvar,
989      true,
990    );
991    self.add_option(
992      "--experimental-quic",
993      "",
994      OptionType::Boolean,
995      OptionEnvvarSettings::AllowedInEnvvar,
996      false,
997    );
998    self.add_option(
999      "--experimental-webstorage",
1000      "experimental Web Storage API",
1001      OptionType::Boolean,
1002      OptionEnvvarSettings::AllowedInEnvvar,
1003      false,
1004    );
1005    self.add_option(
1006      "--localstorage-file",
1007      "file used to persist localStorage data",
1008      OptionType::String,
1009      OptionEnvvarSettings::AllowedInEnvvar,
1010      false,
1011    );
1012    self.add_option(
1013      "--experimental-global-navigator",
1014      "expose experimental Navigator API on the global scope",
1015      OptionType::Boolean,
1016      OptionEnvvarSettings::AllowedInEnvvar,
1017      true,
1018    );
1019    self.add_option(
1020      "--experimental-global-webcrypto",
1021      "",
1022      OptionType::NoOp,
1023      OptionEnvvarSettings::AllowedInEnvvar,
1024      false,
1025    );
1026    self.add_option(
1027      "--experimental-json-modules",
1028      "",
1029      OptionType::NoOp,
1030      OptionEnvvarSettings::AllowedInEnvvar,
1031      false,
1032    );
1033    self.add_option(
1034      "--experimental-loader",
1035      "use the specified module as a custom loader",
1036      OptionType::StringList,
1037      OptionEnvvarSettings::AllowedInEnvvar,
1038      false,
1039    );
1040    self.add_option(
1041      "--experimental-modules",
1042      "",
1043      OptionType::NoOp,
1044      OptionEnvvarSettings::AllowedInEnvvar,
1045      false,
1046    );
1047    self.add_option(
1048      "--experimental-wasm-modules",
1049      "experimental ES Module support for webassembly modules",
1050      OptionType::Boolean,
1051      OptionEnvvarSettings::AllowedInEnvvar,
1052      false,
1053    );
1054    self.add_option(
1055      "--experimental-import-meta-resolve",
1056      "experimental ES Module import.meta.resolve() parentURL support",
1057      OptionType::Boolean,
1058      OptionEnvvarSettings::AllowedInEnvvar,
1059      false,
1060    );
1061    self.add_option(
1062      "--permission",
1063      "enable the permission system",
1064      OptionType::Boolean,
1065      OptionEnvvarSettings::AllowedInEnvvar,
1066      false,
1067    );
1068    self.add_option(
1069      "--allow-fs-read",
1070      "allow permissions to read the filesystem",
1071      OptionType::StringList,
1072      OptionEnvvarSettings::AllowedInEnvvar,
1073      false,
1074    );
1075    self.add_option(
1076      "--allow-fs-write",
1077      "allow permissions to write in the filesystem",
1078      OptionType::StringList,
1079      OptionEnvvarSettings::AllowedInEnvvar,
1080      false,
1081    );
1082    self.add_option(
1083      "--allow-addons",
1084      "allow use of addons when any permissions are set",
1085      OptionType::Boolean,
1086      OptionEnvvarSettings::AllowedInEnvvar,
1087      false,
1088    );
1089    self.add_option(
1090      "--allow-child-process",
1091      "allow use of child process when any permissions are set",
1092      OptionType::Boolean,
1093      OptionEnvvarSettings::AllowedInEnvvar,
1094      false,
1095    );
1096    self.add_option(
1097      "--allow-net",
1098      "allow use of network when any permissions are set",
1099      OptionType::StringList,
1100      OptionEnvvarSettings::AllowedInEnvvar,
1101      false,
1102    );
1103    self.add_option(
1104      "--allow-wasi",
1105      "allow wasi when any permissions are set",
1106      OptionType::Boolean,
1107      OptionEnvvarSettings::AllowedInEnvvar,
1108      false,
1109    );
1110    self.add_option(
1111      "--allow-worker",
1112      "allow worker threads when any permissions are set",
1113      OptionType::Boolean,
1114      OptionEnvvarSettings::AllowedInEnvvar,
1115      false,
1116    );
1117    self.add_option(
1118      "--experimental-repl-await",
1119      "experimental await keyword support in REPL",
1120      OptionType::Boolean,
1121      OptionEnvvarSettings::AllowedInEnvvar,
1122      true,
1123    );
1124    self.add_option(
1125      "--experimental-vm-modules",
1126      "experimental ES Module support in vm module",
1127      OptionType::Boolean,
1128      OptionEnvvarSettings::AllowedInEnvvar,
1129      false,
1130    );
1131    self.add_option(
1132      "--experimental-worker",
1133      "",
1134      OptionType::NoOp,
1135      OptionEnvvarSettings::AllowedInEnvvar,
1136      false,
1137    );
1138    self.add_option(
1139      "--experimental-report",
1140      "",
1141      OptionType::NoOp,
1142      OptionEnvvarSettings::AllowedInEnvvar,
1143      false,
1144    );
1145    self.add_option(
1146      "--experimental-wasi-unstable-preview1",
1147      "",
1148      OptionType::NoOp,
1149      OptionEnvvarSettings::AllowedInEnvvar,
1150      false,
1151    );
1152    self.add_option(
1153      "--expose-gc",
1154      "expose gc extension",
1155      OptionType::V8Option,
1156      OptionEnvvarSettings::AllowedInEnvvar,
1157      false,
1158    );
1159    self.add_option(
1160      "--async-context-frame",
1161      "Improve AsyncLocalStorage performance with AsyncContextFrame",
1162      OptionType::Boolean,
1163      OptionEnvvarSettings::AllowedInEnvvar,
1164      true,
1165    );
1166    self.add_option(
1167      "--expose-internals",
1168      "",
1169      OptionType::Boolean,
1170      OptionEnvvarSettings::DisallowedInEnvvar,
1171      false,
1172    );
1173    self.add_option(
1174      "--frozen-intrinsics",
1175      "experimental frozen intrinsics support",
1176      OptionType::Boolean,
1177      OptionEnvvarSettings::AllowedInEnvvar,
1178      false,
1179    );
1180    self.add_option(
1181      "--heapsnapshot-signal",
1182      "Generate heap snapshot on specified signal",
1183      OptionType::String,
1184      OptionEnvvarSettings::AllowedInEnvvar,
1185      false,
1186    );
1187    self.add_option("--heapsnapshot-near-heap-limit", "Generate heap snapshots whenever V8 is approaching the heap limit. No more than the specified number of heap snapshots will be generated.", OptionType::Integer, OptionEnvvarSettings::AllowedInEnvvar, false);
1188    self.add_option(
1189      "--http-parser",
1190      "",
1191      OptionType::NoOp,
1192      OptionEnvvarSettings::AllowedInEnvvar,
1193      false,
1194    );
1195    self.add_option(
1196      "--insecure-http-parser",
1197      "use an insecure HTTP parser that accepts invalid HTTP headers",
1198      OptionType::Boolean,
1199      OptionEnvvarSettings::AllowedInEnvvar,
1200      false,
1201    );
1202    self.add_option(
1203      "--input-type",
1204      "set module type for string input",
1205      OptionType::String,
1206      OptionEnvvarSettings::AllowedInEnvvar,
1207      false,
1208    );
1209    self.add_option(
1210      "--experimental-specifier-resolution",
1211      "",
1212      OptionType::NoOp,
1213      OptionEnvvarSettings::AllowedInEnvvar,
1214      false,
1215    );
1216    self.add_option(
1217      "--deprecation",
1218      "silence deprecation warnings",
1219      OptionType::Boolean,
1220      OptionEnvvarSettings::AllowedInEnvvar,
1221      true,
1222    );
1223    self.add_option(
1224      "--force-async-hooks-checks",
1225      "disable checks for async_hooks",
1226      OptionType::Boolean,
1227      OptionEnvvarSettings::AllowedInEnvvar,
1228      true,
1229    );
1230    self.add_option(
1231      "--force-node-api-uncaught-exceptions-policy",
1232      "enforces 'uncaughtException' event on Node API asynchronous callbacks",
1233      OptionType::Boolean,
1234      OptionEnvvarSettings::AllowedInEnvvar,
1235      false,
1236    );
1237    self.add_option(
1238      "--addons",
1239      "disable loading native addons",
1240      OptionType::Boolean,
1241      OptionEnvvarSettings::AllowedInEnvvar,
1242      true,
1243    );
1244    self.add_option(
1245      "--global-search-paths",
1246      "disable global module search paths",
1247      OptionType::Boolean,
1248      OptionEnvvarSettings::AllowedInEnvvar,
1249      true,
1250    );
1251    self.add_option(
1252      "--warnings",
1253      "silence all process warnings",
1254      OptionType::Boolean,
1255      OptionEnvvarSettings::AllowedInEnvvar,
1256      true,
1257    );
1258    self.add_option(
1259      "--disable-warning",
1260      "silence specific process warnings",
1261      OptionType::StringList,
1262      OptionEnvvarSettings::AllowedInEnvvar,
1263      false,
1264    );
1265    self.add_option(
1266      "--force-context-aware",
1267      "disable loading non-context-aware addons",
1268      OptionType::Boolean,
1269      OptionEnvvarSettings::AllowedInEnvvar,
1270      false,
1271    );
1272    self.add_option(
1273      "--pending-deprecation",
1274      "emit pending deprecation warnings",
1275      OptionType::Boolean,
1276      OptionEnvvarSettings::AllowedInEnvvar,
1277      false,
1278    );
1279    self.add_option(
1280      "--preserve-symlinks",
1281      "preserve symbolic links when resolving",
1282      OptionType::Boolean,
1283      OptionEnvvarSettings::AllowedInEnvvar,
1284      false,
1285    );
1286    self.add_option(
1287      "--preserve-symlinks-main",
1288      "preserve symbolic links when resolving the main module",
1289      OptionType::Boolean,
1290      OptionEnvvarSettings::AllowedInEnvvar,
1291      false,
1292    );
1293    self.add_option(
1294      "--prof",
1295      "Generate V8 profiler output.",
1296      OptionType::V8Option,
1297      OptionEnvvarSettings::DisallowedInEnvvar,
1298      false,
1299    );
1300    self.add_option(
1301      "--prof-process",
1302      "process V8 profiler output generated using --prof",
1303      OptionType::Boolean,
1304      OptionEnvvarSettings::DisallowedInEnvvar,
1305      false,
1306    );
1307    self.add_option("--cpu-prof", "Start the V8 CPU profiler on start up, and write the CPU profile to disk before exit. If --cpu-prof-dir is not specified, write the profile to the current working directory.", OptionType::Boolean, OptionEnvvarSettings::AllowedInEnvvar, false);
1308    self.add_option(
1309      "--cpu-prof-name",
1310      "specified file name of the V8 CPU profile generated with --cpu-prof",
1311      OptionType::String,
1312      OptionEnvvarSettings::AllowedInEnvvar,
1313      false,
1314    );
1315    self.add_option("--cpu-prof-interval", "specified sampling interval in microseconds for the V8 CPU profile generated with --cpu-prof. (default: 1000)", OptionType::UInteger, OptionEnvvarSettings::AllowedInEnvvar, false);
1316    self.add_option("--cpu-prof-dir", "Directory where the V8 profiles generated by --cpu-prof will be placed. Does not affect --prof.", OptionType::String, OptionEnvvarSettings::AllowedInEnvvar, false);
1317    self.add_option(
1318      "--experimental-network-inspection",
1319      "experimental network inspection support",
1320      OptionType::Boolean,
1321      OptionEnvvarSettings::DisallowedInEnvvar,
1322      false,
1323    );
1324    self.add_option(
1325      "--experimental-worker-inspection",
1326      "experimental worker inspection support",
1327      OptionType::Boolean,
1328      OptionEnvvarSettings::DisallowedInEnvvar,
1329      false,
1330    );
1331    self.add_option("--heap-prof", "Start the V8 heap profiler on start up, and write the heap profile to disk before exit. If --heap-prof-dir is not specified, write the profile to the current working directory.", OptionType::Boolean, OptionEnvvarSettings::AllowedInEnvvar, false);
1332    self.add_option(
1333      "--heap-prof-name",
1334      "specified file name of the V8 heap profile generated with --heap-prof",
1335      OptionType::String,
1336      OptionEnvvarSettings::AllowedInEnvvar,
1337      false,
1338    );
1339    self.add_option(
1340            "--heap-prof-dir",
1341            "Directory where the V8 heap profiles generated by --heap-prof will be placed.",
1342            OptionType::String,
1343            OptionEnvvarSettings::AllowedInEnvvar,
1344            false,
1345        );
1346    self.add_option("--heap-prof-interval", "specified sampling interval in bytes for the V8 heap profile generated with --heap-prof. (default: 512 * 1024)", OptionType::UInteger, OptionEnvvarSettings::AllowedInEnvvar, false);
1347    self.add_option(
1348      "--max-http-header-size",
1349      "set the maximum size of HTTP headers (default: 16384 (16KB))",
1350      OptionType::UInteger,
1351      OptionEnvvarSettings::AllowedInEnvvar,
1352      false,
1353    );
1354    self.add_option(
1355      "--redirect-warnings",
1356      "write warnings to file instead of stderr",
1357      OptionType::String,
1358      OptionEnvvarSettings::AllowedInEnvvar,
1359      false,
1360    );
1361    self.add_option(
1362      "[has_env_file_string]",
1363      "",
1364      OptionType::Boolean,
1365      OptionEnvvarSettings::DisallowedInEnvvar,
1366      false,
1367    );
1368    self.add_option(
1369      "--env-file",
1370      "set environment variables from supplied file",
1371      OptionType::String,
1372      OptionEnvvarSettings::DisallowedInEnvvar,
1373      false,
1374    );
1375    self.add_option(
1376      "--env-file-if-exists",
1377      "set environment variables from supplied file",
1378      OptionType::String,
1379      OptionEnvvarSettings::DisallowedInEnvvar,
1380      false,
1381    );
1382    self.add_option(
1383      "--experimental-config-file",
1384      "set config file from supplied file",
1385      OptionType::String,
1386      OptionEnvvarSettings::DisallowedInEnvvar,
1387      false,
1388    );
1389    self.add_option(
1390      "--experimental-default-config-file",
1391      "set config file from default config file",
1392      OptionType::Boolean,
1393      OptionEnvvarSettings::DisallowedInEnvvar,
1394      false,
1395    );
1396    self.add_option(
1397      "--test",
1398      "launch test runner on startup",
1399      OptionType::Boolean,
1400      OptionEnvvarSettings::DisallowedInEnvvar,
1401      false,
1402    );
1403    self.add_option(
1404      "--test-concurrency",
1405      "specify test runner concurrency",
1406      OptionType::UInteger,
1407      OptionEnvvarSettings::DisallowedInEnvvar,
1408      false,
1409    );
1410    self.add_option(
1411      "--test-force-exit",
1412      "force test runner to exit upon completion",
1413      OptionType::Boolean,
1414      OptionEnvvarSettings::DisallowedInEnvvar,
1415      false,
1416    );
1417    self.add_option(
1418      "--test-timeout",
1419      "specify test runner timeout",
1420      OptionType::UInteger,
1421      OptionEnvvarSettings::DisallowedInEnvvar,
1422      false,
1423    );
1424    self.add_option(
1425      "--test-update-snapshots",
1426      "regenerate test snapshots",
1427      OptionType::Boolean,
1428      OptionEnvvarSettings::DisallowedInEnvvar,
1429      false,
1430    );
1431    self.add_option(
1432      "--experimental-test-coverage",
1433      "enable code coverage in the test runner",
1434      OptionType::Boolean,
1435      OptionEnvvarSettings::DisallowedInEnvvar,
1436      false,
1437    );
1438    self.add_option(
1439      "--test-coverage-branches",
1440      "the branch coverage minimum threshold",
1441      OptionType::UInteger,
1442      OptionEnvvarSettings::AllowedInEnvvar,
1443      false,
1444    );
1445    self.add_option(
1446      "--test-coverage-functions",
1447      "the function coverage minimum threshold",
1448      OptionType::UInteger,
1449      OptionEnvvarSettings::AllowedInEnvvar,
1450      false,
1451    );
1452    self.add_option(
1453      "--test-coverage-lines",
1454      "the line coverage minimum threshold",
1455      OptionType::UInteger,
1456      OptionEnvvarSettings::AllowedInEnvvar,
1457      false,
1458    );
1459    self.add_option(
1460      "--test-isolation",
1461      "configures the type of test isolation used in the test runner",
1462      OptionType::String,
1463      OptionEnvvarSettings::AllowedInEnvvar,
1464      false,
1465    );
1466    self.add_option(
1467      "--experimental-test-module-mocks",
1468      "enable module mocking in the test runner",
1469      OptionType::Boolean,
1470      OptionEnvvarSettings::DisallowedInEnvvar,
1471      false,
1472    );
1473    self.add_option(
1474      "--experimental-test-snapshots",
1475      "",
1476      OptionType::NoOp,
1477      OptionEnvvarSettings::DisallowedInEnvvar,
1478      false,
1479    );
1480    self.add_option(
1481      "--test-name-pattern",
1482      "run tests whose name matches this regular expression",
1483      OptionType::StringList,
1484      OptionEnvvarSettings::AllowedInEnvvar,
1485      false,
1486    );
1487    self.add_option(
1488      "--test-reporter",
1489      "report test output using the given reporter",
1490      OptionType::StringList,
1491      OptionEnvvarSettings::AllowedInEnvvar,
1492      false,
1493    );
1494    self.add_option(
1495      "--test-reporter-destination",
1496      "report given reporter to the given destination",
1497      OptionType::StringList,
1498      OptionEnvvarSettings::AllowedInEnvvar,
1499      false,
1500    );
1501    self.add_option(
1502      "--test-only",
1503      "run tests with 'only' option set",
1504      OptionType::Boolean,
1505      OptionEnvvarSettings::AllowedInEnvvar,
1506      false,
1507    );
1508    self.add_option(
1509      "--test-shard",
1510      "run test at specific shard",
1511      OptionType::String,
1512      OptionEnvvarSettings::AllowedInEnvvar,
1513      false,
1514    );
1515    self.add_option(
1516      "--test-skip-pattern",
1517      "run tests whose name do not match this regular expression",
1518      OptionType::StringList,
1519      OptionEnvvarSettings::AllowedInEnvvar,
1520      false,
1521    );
1522    self.add_option(
1523      "--test-coverage-include",
1524      "include files in coverage report that match this glob pattern",
1525      OptionType::StringList,
1526      OptionEnvvarSettings::AllowedInEnvvar,
1527      false,
1528    );
1529    self.add_option(
1530      "--test-coverage-exclude",
1531      "exclude files from coverage report that match this glob pattern",
1532      OptionType::StringList,
1533      OptionEnvvarSettings::AllowedInEnvvar,
1534      false,
1535    );
1536    self.add_option(
1537      "--test-global-setup",
1538      "specifies the path to the global setup file",
1539      OptionType::String,
1540      OptionEnvvarSettings::AllowedInEnvvar,
1541      false,
1542    );
1543    self.add_option(
1544      "--test-udp-no-try-send",
1545      "",
1546      OptionType::Boolean,
1547      OptionEnvvarSettings::DisallowedInEnvvar,
1548      false,
1549    );
1550    self.add_option(
1551      "--throw-deprecation",
1552      "throw an exception on deprecations",
1553      OptionType::Boolean,
1554      OptionEnvvarSettings::AllowedInEnvvar,
1555      false,
1556    );
1557    self.add_option(
1558      "--trace-deprecation",
1559      "show stack traces on deprecations",
1560      OptionType::Boolean,
1561      OptionEnvvarSettings::AllowedInEnvvar,
1562      false,
1563    );
1564    self.add_option(
1565      "--trace-exit",
1566      "show stack trace when an environment exits",
1567      OptionType::Boolean,
1568      OptionEnvvarSettings::AllowedInEnvvar,
1569      false,
1570    );
1571    self.add_option(
1572      "--trace-sync-io",
1573      "show stack trace when use of sync IO is detected after the first tick",
1574      OptionType::Boolean,
1575      OptionEnvvarSettings::AllowedInEnvvar,
1576      false,
1577    );
1578    self.add_option(
1579      "--trace-tls",
1580      "prints TLS packet trace information to stderr",
1581      OptionType::Boolean,
1582      OptionEnvvarSettings::AllowedInEnvvar,
1583      false,
1584    );
1585    self.add_option(
1586      "--trace-uncaught",
1587      "show stack traces for the `throw` behind uncaught exceptions",
1588      OptionType::Boolean,
1589      OptionEnvvarSettings::AllowedInEnvvar,
1590      false,
1591    );
1592    self.add_option(
1593      "--trace-warnings",
1594      "show stack traces on process warnings",
1595      OptionType::Boolean,
1596      OptionEnvvarSettings::AllowedInEnvvar,
1597      false,
1598    );
1599    self.add_option(
1600      "--trace-promises",
1601      "show stack traces on promise initialization and resolution",
1602      OptionType::Boolean,
1603      OptionEnvvarSettings::AllowedInEnvvar,
1604      false,
1605    );
1606    self.add_option(
1607      "--trace-env",
1608      "Print accesses to the environment variables",
1609      OptionType::Boolean,
1610      OptionEnvvarSettings::AllowedInEnvvar,
1611      false,
1612    );
1613    self.add_option(
1614            "--trace-env-js-stack",
1615            "Print accesses to the environment variables and the JavaScript stack trace",
1616            OptionType::Boolean,
1617            OptionEnvvarSettings::AllowedInEnvvar,
1618            false,
1619        );
1620    self.add_option(
1621      "--trace-env-native-stack",
1622      "Print accesses to the environment variables and the native stack trace",
1623      OptionType::Boolean,
1624      OptionEnvvarSettings::AllowedInEnvvar,
1625      false,
1626    );
1627    self.add_option("--trace-require-module", "Print access to require(esm). Options are 'all' (print all usage) and 'no-node-modules' (excluding usage from the node_modules folder)", OptionType::String, OptionEnvvarSettings::AllowedInEnvvar, false);
1628    self.add_option(
1629      "--extra-info-on-fatal-exception",
1630      "hide extra information on fatal exception that causes exit",
1631      OptionType::Boolean,
1632      OptionEnvvarSettings::AllowedInEnvvar,
1633      true,
1634    );
1635    self.add_option("--unhandled-rejections", "define unhandled rejections behavior. Options are 'strict' (always raise an error), 'throw' (raise an error unless 'unhandledRejection' hook is set), 'warn' (log a warning), 'none' (silence warnings), 'warn-with-error-code' (log a warning and set exit code 1 unless 'unhandledRejection' hook is set). (default: throw)", OptionType::String, OptionEnvvarSettings::AllowedInEnvvar, false);
1636    self.add_option(
1637      "--verify-base-objects",
1638      "",
1639      OptionType::Boolean,
1640      OptionEnvvarSettings::AllowedInEnvvar,
1641      false,
1642    );
1643    self.add_option(
1644      "--watch",
1645      "run in watch mode",
1646      OptionType::Boolean,
1647      OptionEnvvarSettings::AllowedInEnvvar,
1648      false,
1649    );
1650    self.add_option(
1651      "--watch-path",
1652      "path to watch",
1653      OptionType::StringList,
1654      OptionEnvvarSettings::AllowedInEnvvar,
1655      false,
1656    );
1657    self.add_option(
1658            "--watch-kill-signal",
1659            "kill signal to send to the process on watch mode restarts (default: SIGTERM)",
1660            OptionType::String,
1661            OptionEnvvarSettings::AllowedInEnvvar,
1662            false,
1663        );
1664    self.add_option(
1665      "--watch-preserve-output",
1666      "preserve outputs on watch mode restart",
1667      OptionType::Boolean,
1668      OptionEnvvarSettings::AllowedInEnvvar,
1669      false,
1670    );
1671    self.add_option(
1672      "--check",
1673      "syntax check script without executing",
1674      OptionType::Boolean,
1675      OptionEnvvarSettings::DisallowedInEnvvar,
1676      false,
1677    );
1678    self.add_option(
1679      "[has_eval_string]",
1680      "",
1681      OptionType::Boolean,
1682      OptionEnvvarSettings::DisallowedInEnvvar,
1683      false,
1684    );
1685    self.add_option(
1686      "--eval",
1687      "evaluate script",
1688      OptionType::String,
1689      OptionEnvvarSettings::DisallowedInEnvvar,
1690      false,
1691    );
1692    self.add_option(
1693      "--print",
1694      "evaluate script and print result",
1695      OptionType::Boolean,
1696      OptionEnvvarSettings::DisallowedInEnvvar,
1697      false,
1698    );
1699    self.add_option(
1700      "--require",
1701      "CommonJS module to preload (option can be repeated)",
1702      OptionType::StringList,
1703      OptionEnvvarSettings::AllowedInEnvvar,
1704      false,
1705    );
1706    self.add_option(
1707      "--import",
1708      "ES module to preload (option can be repeated)",
1709      OptionType::StringList,
1710      OptionEnvvarSettings::AllowedInEnvvar,
1711      false,
1712    );
1713    self.add_option(
1714      "--experimental-strip-types",
1715      "Experimental type-stripping for TypeScript files.",
1716      OptionType::Boolean,
1717      OptionEnvvarSettings::AllowedInEnvvar,
1718      true,
1719    );
1720    self.add_option(
1721      "--experimental-transform-types",
1722      "enable transformation of TypeScript-only syntax into JavaScript code",
1723      OptionType::Boolean,
1724      OptionEnvvarSettings::AllowedInEnvvar,
1725      false,
1726    );
1727    self.add_option(
1728      "--interactive",
1729      "always enter the REPL even if stdin does not appear to be a terminal",
1730      OptionType::Boolean,
1731      OptionEnvvarSettings::DisallowedInEnvvar,
1732      false,
1733    );
1734    self.add_option(
1735      "--napi-modules",
1736      "",
1737      OptionType::NoOp,
1738      OptionEnvvarSettings::AllowedInEnvvar,
1739      false,
1740    );
1741    self.add_option(
1742      "--tls-keylog",
1743      "log TLS decryption keys to named file for traffic analysis",
1744      OptionType::String,
1745      OptionEnvvarSettings::AllowedInEnvvar,
1746      false,
1747    );
1748    self.add_option(
1749      "--tls-min-v1.0",
1750      "set default TLS minimum to TLSv1.0 (default: TLSv1.2)",
1751      OptionType::Boolean,
1752      OptionEnvvarSettings::AllowedInEnvvar,
1753      false,
1754    );
1755    self.add_option(
1756      "--tls-min-v1.1",
1757      "set default TLS minimum to TLSv1.1 (default: TLSv1.2)",
1758      OptionType::Boolean,
1759      OptionEnvvarSettings::AllowedInEnvvar,
1760      false,
1761    );
1762    self.add_option(
1763      "--tls-min-v1.2",
1764      "set default TLS minimum to TLSv1.2 (default: TLSv1.2)",
1765      OptionType::Boolean,
1766      OptionEnvvarSettings::AllowedInEnvvar,
1767      false,
1768    );
1769    self.add_option(
1770      "--tls-min-v1.3",
1771      "set default TLS minimum to TLSv1.3 (default: TLSv1.2)",
1772      OptionType::Boolean,
1773      OptionEnvvarSettings::AllowedInEnvvar,
1774      false,
1775    );
1776    self.add_option(
1777      "--tls-max-v1.2",
1778      "set default TLS maximum to TLSv1.2 (default: TLSv1.3)",
1779      OptionType::Boolean,
1780      OptionEnvvarSettings::AllowedInEnvvar,
1781      false,
1782    );
1783    self.add_option(
1784      "--tls-max-v1.3",
1785      "set default TLS maximum to TLSv1.3 (default: TLSv1.3)",
1786      OptionType::Boolean,
1787      OptionEnvvarSettings::AllowedInEnvvar,
1788      false,
1789    );
1790    self.add_option(
1791      "--report-exclude-env",
1792      "Exclude environment variables when generating report (default: false)",
1793      OptionType::Boolean,
1794      OptionEnvvarSettings::AllowedInEnvvar,
1795      false,
1796    );
1797    self.add_option(
1798      "--report-exclude-network",
1799      "exclude network interface diagnostics. (default: false)",
1800      OptionType::Boolean,
1801      OptionEnvvarSettings::AllowedInEnvvar,
1802      false,
1803    );
1804
1805    // Per-isolate options
1806    self.add_option(
1807      "--track-heap-objects",
1808      "track heap object allocations for heap snapshots",
1809      OptionType::Boolean,
1810      OptionEnvvarSettings::AllowedInEnvvar,
1811      false,
1812    );
1813    self.add_option(
1814            "--abort-on-uncaught-exception",
1815            "aborting instead of exiting causes a core file to be generated for analysis",
1816            OptionType::V8Option,
1817            OptionEnvvarSettings::AllowedInEnvvar,
1818            false,
1819        );
1820    self.add_option(
1821      "--interpreted-frames-native-stack",
1822      "help system profilers to translate JavaScript interpreted frames",
1823      OptionType::V8Option,
1824      OptionEnvvarSettings::AllowedInEnvvar,
1825      false,
1826    );
1827    self.add_option(
1828      "--max-old-space-size",
1829      "",
1830      OptionType::V8Option,
1831      OptionEnvvarSettings::AllowedInEnvvar,
1832      false,
1833    );
1834    self.add_option(
1835      "--max-semi-space-size",
1836      "",
1837      OptionType::V8Option,
1838      OptionEnvvarSettings::AllowedInEnvvar,
1839      false,
1840    );
1841    self.add_option(
1842      "--perf-basic-prof",
1843      "",
1844      OptionType::V8Option,
1845      OptionEnvvarSettings::AllowedInEnvvar,
1846      false,
1847    );
1848    self.add_option(
1849      "--perf-basic-prof-only-functions",
1850      "",
1851      OptionType::V8Option,
1852      OptionEnvvarSettings::AllowedInEnvvar,
1853      false,
1854    );
1855    self.add_option(
1856      "--perf-prof",
1857      "",
1858      OptionType::V8Option,
1859      OptionEnvvarSettings::AllowedInEnvvar,
1860      false,
1861    );
1862    self.add_option(
1863      "--perf-prof-unwinding-info",
1864      "",
1865      OptionType::V8Option,
1866      OptionEnvvarSettings::AllowedInEnvvar,
1867      false,
1868    );
1869    self.add_option(
1870      "--stack-trace-limit",
1871      "",
1872      OptionType::Integer,
1873      OptionEnvvarSettings::AllowedInEnvvar,
1874      false,
1875    );
1876    self.add_option(
1877      "--disallow-code-generation-from-strings",
1878      "disallow eval and friends",
1879      OptionType::V8Option,
1880      OptionEnvvarSettings::AllowedInEnvvar,
1881      false,
1882    );
1883    self.add_option(
1884      "--jitless",
1885      "disable runtime allocation of executable memory",
1886      OptionType::V8Option,
1887      OptionEnvvarSettings::AllowedInEnvvar,
1888      false,
1889    );
1890    self.add_option(
1891      "--report-uncaught-exception",
1892      "generate diagnostic report on uncaught exceptions",
1893      OptionType::Boolean,
1894      OptionEnvvarSettings::AllowedInEnvvar,
1895      false,
1896    );
1897    self.add_option(
1898      "--report-on-signal",
1899      "generate diagnostic report upon receiving signals",
1900      OptionType::Boolean,
1901      OptionEnvvarSettings::AllowedInEnvvar,
1902      false,
1903    );
1904    self.add_option("--report-signal", "causes diagnostic report to be produced on provided signal, unsupported in Windows. (default: SIGUSR2)", OptionType::String, OptionEnvvarSettings::AllowedInEnvvar, false);
1905    self.add_option(
1906      "--enable-etw-stack-walking",
1907      "provides heap data to ETW Windows native tracing",
1908      OptionType::V8Option,
1909      OptionEnvvarSettings::AllowedInEnvvar,
1910      false,
1911    );
1912    self.add_option(
1913      "--experimental-top-level-await",
1914      "",
1915      OptionType::NoOp,
1916      OptionEnvvarSettings::AllowedInEnvvar,
1917      false,
1918    );
1919    self.add_option(
1920      "--experimental-shadow-realm",
1921      "",
1922      OptionType::Boolean,
1923      OptionEnvvarSettings::AllowedInEnvvar,
1924      false,
1925    );
1926    self.add_option(
1927      "--harmony-shadow-realm",
1928      "",
1929      OptionType::V8Option,
1930      OptionEnvvarSettings::DisallowedInEnvvar,
1931      false,
1932    );
1933    self.add_option(
1934      "--build-snapshot",
1935      "Generate a snapshot blob when the process exits.",
1936      OptionType::Boolean,
1937      OptionEnvvarSettings::DisallowedInEnvvar,
1938      false,
1939    );
1940    self.add_option("--build-snapshot-config", "Generate a snapshot blob when the process exits using a JSON configuration in the specified path.", OptionType::String, OptionEnvvarSettings::DisallowedInEnvvar, false);
1941
1942    // Per-process options
1943    self.add_option(
1944      "--title",
1945      "the process title to use on startup",
1946      OptionType::String,
1947      OptionEnvvarSettings::AllowedInEnvvar,
1948      false,
1949    );
1950    self.add_option(
1951      "--trace-event-categories",
1952      "comma separated list of trace event categories to record",
1953      OptionType::String,
1954      OptionEnvvarSettings::AllowedInEnvvar,
1955      false,
1956    );
1957    self.add_option("--trace-event-file-pattern", "Template string specifying the filepath for the trace-events data, it supports ${rotation} and ${pid}.", OptionType::String, OptionEnvvarSettings::AllowedInEnvvar, false);
1958    self.add_option(
1959      "--v8-pool-size",
1960      "set V8's thread pool size",
1961      OptionType::Integer,
1962      OptionEnvvarSettings::AllowedInEnvvar,
1963      false,
1964    );
1965    self.add_option(
1966      "--zero-fill-buffers",
1967      "automatically zero-fill all newly allocated Buffer instances",
1968      OptionType::Boolean,
1969      OptionEnvvarSettings::AllowedInEnvvar,
1970      false,
1971    );
1972    self.add_option(
1973      "--debug-arraybuffer-allocations",
1974      "",
1975      OptionType::Boolean,
1976      OptionEnvvarSettings::AllowedInEnvvar,
1977      false,
1978    );
1979    self.add_option(
1980      "--disable-proto",
1981      "disable Object.prototype.__proto__",
1982      OptionType::String,
1983      OptionEnvvarSettings::AllowedInEnvvar,
1984      false,
1985    );
1986    self.add_option(
1987      "--node-snapshot",
1988      "",
1989      OptionType::Boolean,
1990      OptionEnvvarSettings::AllowedInEnvvar,
1991      false,
1992    );
1993    self.add_option("--snapshot-blob", "Path to the snapshot blob that's either the result of snapshot building, or the blob that is used to restore the application state", OptionType::String, OptionEnvvarSettings::AllowedInEnvvar, false);
1994    self.add_option(
1995      "--security-revert",
1996      "",
1997      OptionType::StringList,
1998      OptionEnvvarSettings::DisallowedInEnvvar,
1999      false,
2000    );
2001    self.add_option(
2002      "--completion-bash",
2003      "print source-able bash completion script",
2004      OptionType::Boolean,
2005      OptionEnvvarSettings::DisallowedInEnvvar,
2006      false,
2007    );
2008    self.add_option(
2009      "--help",
2010      "print node command line options",
2011      OptionType::Boolean,
2012      OptionEnvvarSettings::DisallowedInEnvvar,
2013      false,
2014    );
2015    self.add_option(
2016      "--version",
2017      "print Node.js version",
2018      OptionType::Boolean,
2019      OptionEnvvarSettings::DisallowedInEnvvar,
2020      false,
2021    );
2022    self.add_option(
2023      "--v8-options",
2024      "print V8 command line options",
2025      OptionType::Boolean,
2026      OptionEnvvarSettings::DisallowedInEnvvar,
2027      false,
2028    );
2029    self.add_option(
2030      "--report-compact",
2031      "output compact single-line JSON",
2032      OptionType::Boolean,
2033      OptionEnvvarSettings::AllowedInEnvvar,
2034      false,
2035    );
2036    self.add_option(
2037      "--report-dir",
2038      "define custom report pathname. (default: current working directory)",
2039      OptionType::String,
2040      OptionEnvvarSettings::AllowedInEnvvar,
2041      false,
2042    );
2043    self.add_option(
2044            "--report-filename",
2045            "define custom report file name. (default: YYYYMMDD.HHMMSS.PID.SEQUENCE#.txt)",
2046            OptionType::String,
2047            OptionEnvvarSettings::AllowedInEnvvar,
2048            false,
2049        );
2050    self.add_option(
2051      "--report-on-fatalerror",
2052      "generate diagnostic report on fatal (internal) errors",
2053      OptionType::Boolean,
2054      OptionEnvvarSettings::AllowedInEnvvar,
2055      false,
2056    );
2057    self.add_option(
2058      "--icu-data-dir",
2059      "set ICU data load path to dir (overrides NODE_ICU_DATA)",
2060      OptionType::String,
2061      OptionEnvvarSettings::AllowedInEnvvar,
2062      false,
2063    );
2064    self.add_option(
2065            "--openssl-config",
2066            "load OpenSSL configuration from the specified file (overrides OPENSSL_CONF)",
2067            OptionType::String,
2068            OptionEnvvarSettings::AllowedInEnvvar,
2069            false,
2070        );
2071    self.add_option(
2072      "--tls-cipher-list",
2073      "use an alternative default TLS cipher list",
2074      OptionType::String,
2075      OptionEnvvarSettings::AllowedInEnvvar,
2076      false,
2077    );
2078    self.add_option(
2079      "--use-openssl-ca",
2080      "use OpenSSL's default CA store",
2081      OptionType::Boolean,
2082      OptionEnvvarSettings::AllowedInEnvvar,
2083      false,
2084    );
2085    self.add_option(
2086      "--use-system-ca",
2087      "use system's CA store",
2088      OptionType::Boolean,
2089      OptionEnvvarSettings::AllowedInEnvvar,
2090      false,
2091    );
2092    self.add_option(
2093      "--use-bundled-ca",
2094      "use bundled CA store",
2095      OptionType::Boolean,
2096      OptionEnvvarSettings::AllowedInEnvvar,
2097      false,
2098    );
2099    self.add_option(
2100      "[ssl_openssl_cert_store]",
2101      "",
2102      OptionType::Boolean,
2103      OptionEnvvarSettings::DisallowedInEnvvar,
2104      false,
2105    );
2106    self.add_option(
2107      "--enable-fips",
2108      "enable FIPS crypto at startup",
2109      OptionType::Boolean,
2110      OptionEnvvarSettings::AllowedInEnvvar,
2111      false,
2112    );
2113    self.add_option(
2114      "--force-fips",
2115      "force FIPS crypto (cannot be disabled)",
2116      OptionType::Boolean,
2117      OptionEnvvarSettings::AllowedInEnvvar,
2118      false,
2119    );
2120    self.add_option(
2121      "--secure-heap",
2122      "total size of the OpenSSL secure heap",
2123      OptionType::Integer,
2124      OptionEnvvarSettings::AllowedInEnvvar,
2125      false,
2126    );
2127    self.add_option(
2128      "--secure-heap-min",
2129      "minimum allocation size from the OpenSSL secure heap",
2130      OptionType::Integer,
2131      OptionEnvvarSettings::AllowedInEnvvar,
2132      false,
2133    );
2134    self.add_option(
2135      "--openssl-legacy-provider",
2136      "enable OpenSSL 3.0 legacy provider",
2137      OptionType::Boolean,
2138      OptionEnvvarSettings::AllowedInEnvvar,
2139      false,
2140    );
2141    self.add_option(
2142      "--openssl-shared-config",
2143      "enable OpenSSL shared configuration",
2144      OptionType::Boolean,
2145      OptionEnvvarSettings::AllowedInEnvvar,
2146      false,
2147    );
2148    self.add_option("--use-largepages", "Map the Node.js static code to large pages. Options are 'off' (the default value, meaning do not map), 'on' (map and ignore failure, reporting it to stderr), or 'silent' (map and silently ignore failure)", OptionType::String, OptionEnvvarSettings::AllowedInEnvvar, false);
2149    self.add_option(
2150      "--trace-sigint",
2151      "enable printing JavaScript stacktrace on SIGINT",
2152      OptionType::Boolean,
2153      OptionEnvvarSettings::AllowedInEnvvar,
2154      false,
2155    );
2156    self.add_option(
2157      "--node-memory-debug",
2158      "Run with extra debug checks for memory leaks in Node.js itself",
2159      OptionType::NoOp,
2160      OptionEnvvarSettings::AllowedInEnvvar,
2161      false,
2162    );
2163    self.add_option(
2164            "--experimental-sea-config",
2165            "Generate a blob that can be embedded into the single executable application",
2166            OptionType::String,
2167            OptionEnvvarSettings::DisallowedInEnvvar,
2168            false,
2169        );
2170    self.add_option(
2171      "--run",
2172      "Run a script specified in package.json",
2173      OptionType::String,
2174      OptionEnvvarSettings::DisallowedInEnvvar,
2175      false,
2176    );
2177    self.add_option("--disable-wasm-trap-handler", "Disable trap-handler-based WebAssembly bound checks. V8 will insert inline bound checks when compiling WebAssembly which may slow down performance.", OptionType::Boolean, OptionEnvvarSettings::AllowedInEnvvar, false);
2178
2179    // Setup aliases
2180    self.add_alias("--debug-port", vec!["--inspect-port"]);
2181    self.add_alias("--inspect=", vec!["--inspect-port", "--inspect"]);
2182    self.add_alias("--debug=", vec!["--debug"]);
2183    self.add_alias("--debug-brk=", vec!["--debug-brk"]);
2184    self.add_alias("--inspect-brk=", vec!["--inspect-port", "--inspect-brk"]);
2185    self.add_alias(
2186      "--inspect-brk-node=",
2187      vec!["--inspect-port", "--inspect-brk-node"],
2188    );
2189    self.add_alias("--inspect-wait=", vec!["--inspect-port", "--inspect-wait"]);
2190    self.add_alias("-C", vec!["--conditions"]);
2191    self.add_alias("--loader", vec!["--experimental-loader"]);
2192    self.add_alias(
2193      "--enable-network-family-autoselection",
2194      vec!["--network-family-autoselection"],
2195    );
2196    self.add_alias(
2197      "--es-module-specifier-resolution",
2198      vec!["--experimental-specifier-resolution"],
2199    );
2200    self.add_alias("--prof-process", vec!["--prof-process", "--"]);
2201    self.add_alias("--experimental-test-isolation", vec!["--test-isolation"]);
2202    self.add_alias("-c", vec!["--check"]);
2203    self.add_alias("-e", vec!["--eval"]);
2204    self.add_alias("--print <arg>", vec!["-pe"]);
2205    self.add_alias("-pe", vec!["--print", "--eval"]);
2206    self.add_alias("-p", vec!["--print"]);
2207    self.add_alias("-r", vec!["--require"]);
2208    self.add_alias("-i", vec!["--interactive"]);
2209    self.add_alias("--security-reverts", vec!["--security-revert"]);
2210    self.add_alias("-h", vec!["--help"]);
2211    self.add_alias("-v", vec!["--version"]);
2212    self.add_alias("--report-directory", vec!["--report-dir"]);
2213    self.add_alias(
2214      "--trace-events-enabled",
2215      vec!["--trace-event-categories", "v8,node,node.async_hooks"],
2216    );
2217
2218    // Setup implications
2219    self.add_implication("--inspect-brk", vec!["--inspect"]);
2220    self.add_implication("--inspect-brk-node", vec!["--inspect"]);
2221    self.add_implication("--inspect-wait", vec!["--inspect"]);
2222    self.add_implication("--env-file", vec!["[has_env_file_string]"]);
2223    self.add_implication("--env-file-if-exists", vec!["[has_env_file_string]"]);
2224    self.add_implication("--eval", vec!["[has_eval_string]"]);
2225    self.add_implication(
2226      "--experimental-transform-types",
2227      vec!["--experimental-strip-types", "--enable-source-maps"],
2228    );
2229    self.add_implication("--watch-path", vec!["--watch"]);
2230    self.add_implication("--trace-env-js-stack", vec!["--trace-env"]);
2231    self.add_implication("--trace-env-native-stack", vec!["--trace-env"]);
2232    self.add_implication("--report-signal", vec!["--report-on-signal"]);
2233    self.add_implication(
2234      "--experimental-shadow-realm",
2235      vec!["--harmony-shadow-realm"],
2236    );
2237    self.add_implication(
2238      "--harmony-shadow-realm",
2239      vec!["--experimental-shadow-realm"],
2240    );
2241    self.add_implication("--build-snapshot-config", vec!["--build-snapshot"]);
2242    self.add_implication("--use-openssl-ca", vec!["[ssl_openssl_cert_store]"]);
2243    self.add_implication(
2244      "--node-memory-debug",
2245      vec!["--debug-arraybuffer-allocations", "--verify-base-objects"],
2246    );
2247  }
2248
2249  fn apply_option_value(
2250    &self,
2251    options: &mut PerProcessOptions,
2252    name: &str,
2253    value: &str,
2254    is_negation: bool,
2255    option_info: &OptionInfo,
2256    errors: &mut Vec<String>,
2257  ) {
2258    match option_info.option_type {
2259      OptionType::Boolean => {
2260        self.set_boolean_field(options, name, !is_negation);
2261      }
2262      OptionType::Integer => {
2263        if let Ok(val) = value.parse::<i64>() {
2264          self.set_integer_field(options, name, val);
2265        } else {
2266          errors.push(format!("Invalid integer value for {}: {}", name, value));
2267        }
2268      }
2269      OptionType::UInteger => {
2270        if let Ok(val) = value.parse::<u64>() {
2271          self.set_uinteger_field(options, name, val);
2272        } else {
2273          errors.push(format!(
2274            "Invalid unsigned integer value for {}: {}",
2275            name, value
2276          ));
2277        }
2278      }
2279      OptionType::String => {
2280        self.set_string_field(options, name, value.to_string());
2281      }
2282      OptionType::StringList => {
2283        self.add_to_string_list_field(options, name, value.to_string());
2284      }
2285      OptionType::HostPort => {
2286        let host_port = split_host_port(value, errors);
2287        self.set_host_port_field(options, name, host_port);
2288      }
2289      OptionType::NoOp | OptionType::V8Option => {
2290        // No-op or handled elsewhere
2291      }
2292    }
2293  }
2294
2295  fn set_boolean_field(
2296    &self,
2297    options: &mut PerProcessOptions,
2298    name: &str,
2299    value: bool,
2300  ) {
2301    match name {
2302      // Debug options
2303      "--inspect" => {
2304        options.per_isolate.per_env.debug_options.inspector_enabled = value
2305      }
2306      "--inspect-wait" => {
2307        options.per_isolate.per_env.debug_options.inspect_wait = value
2308      }
2309      "--debug" | "--debug-brk" => {
2310        options.per_isolate.per_env.debug_options.deprecated_debug = value
2311      }
2312      "--inspect-brk" => {
2313        options.per_isolate.per_env.debug_options.break_first_line = value
2314      }
2315      "--inspect-brk-node" => {
2316        options
2317          .per_isolate
2318          .per_env
2319          .debug_options
2320          .break_node_first_line = value
2321      }
2322
2323      // Environment options
2324      "--experimental-detect-module" => {
2325        options.per_isolate.per_env.detect_module = value
2326      }
2327      "--disable-sigusr1" => {
2328        options.per_isolate.per_env.disable_sigusr1 = value
2329      }
2330      "--experimental-print-required-tla" => {
2331        options.per_isolate.per_env.print_required_tla = value
2332      }
2333      "--experimental-require-module" => {
2334        options.per_isolate.per_env.require_module = value
2335      }
2336      "--enable-source-maps" => {
2337        options.per_isolate.per_env.enable_source_maps = value
2338      }
2339      "--entry-url" => options.per_isolate.per_env.entry_is_url = value,
2340      "--experimental-addon-modules" => {
2341        options.per_isolate.per_env.experimental_addon_modules = value
2342      }
2343      "--experimental-eventsource" => {
2344        options.per_isolate.per_env.experimental_eventsource = value
2345      }
2346      "--experimental-websocket" => {
2347        options.per_isolate.per_env.experimental_websocket = value
2348      }
2349      "--experimental-sqlite" => {
2350        options.per_isolate.per_env.experimental_sqlite = value
2351      }
2352      "--experimental-quic" => {
2353        options.per_isolate.per_env.experimental_quic = value
2354      }
2355      "--experimental-webstorage" => {
2356        options.per_isolate.per_env.experimental_webstorage = value
2357      }
2358      "--experimental-global-navigator" => {
2359        options.per_isolate.per_env.experimental_global_navigator = value
2360      }
2361      "--experimental-wasm-modules" => {
2362        options.per_isolate.per_env.experimental_wasm_modules = value
2363      }
2364      "--experimental-import-meta-resolve" => {
2365        options.per_isolate.per_env.experimental_import_meta_resolve = value
2366      }
2367      "--permission" => options.per_isolate.per_env.permission = value,
2368      "--allow-addons" => options.per_isolate.per_env.allow_addons = value,
2369      "--allow-child-process" => {
2370        options.per_isolate.per_env.allow_child_process = value
2371      }
2372      "--allow-wasi" => options.per_isolate.per_env.allow_wasi = value,
2373      "--allow-worker" => {
2374        options.per_isolate.per_env.allow_worker_threads = value
2375      }
2376      "--experimental-repl-await" => {
2377        options.per_isolate.per_env.experimental_repl_await = value
2378      }
2379      "--experimental-vm-modules" => {
2380        options.per_isolate.per_env.experimental_vm_modules = value
2381      }
2382      "--async-context-frame" => {
2383        options.per_isolate.per_env.async_context_frame = value
2384      }
2385      "--expose-internals" => {
2386        options.per_isolate.per_env.expose_internals = value
2387      }
2388      "--frozen-intrinsics" => {
2389        options.per_isolate.per_env.frozen_intrinsics = value
2390      }
2391      "--network-family-autoselection" => {
2392        options.per_isolate.per_env.network_family_autoselection = value
2393      }
2394      "--deprecation" => options.per_isolate.per_env.deprecation = value,
2395      "--force-async-hooks-checks" => {
2396        options.per_isolate.per_env.force_async_hooks_checks = value
2397      }
2398      "--force-node-api-uncaught-exceptions-policy" => {
2399        options
2400          .per_isolate
2401          .per_env
2402          .force_node_api_uncaught_exceptions_policy = value
2403      }
2404      "--addons" => options.per_isolate.per_env.allow_native_addons = value,
2405      "--global-search-paths" => {
2406        options.per_isolate.per_env.global_search_paths = value
2407      }
2408      "--warnings" => options.per_isolate.per_env.warnings = value,
2409      "--force-context-aware" => {
2410        options.per_isolate.per_env.force_context_aware = value
2411      }
2412      "--pending-deprecation" => {
2413        options.per_isolate.per_env.pending_deprecation = value
2414      }
2415      "--preserve-symlinks" => {
2416        options.per_isolate.per_env.preserve_symlinks = value
2417      }
2418      "--preserve-symlinks-main" => {
2419        options.per_isolate.per_env.preserve_symlinks_main = value
2420      }
2421      "--prof-process" => options.per_isolate.per_env.prof_process = value,
2422      "--cpu-prof" => options.per_isolate.per_env.cpu_prof = value,
2423      "--experimental-network-inspection" => {
2424        options.per_isolate.per_env.experimental_network_inspection = value
2425      }
2426      "--experimental-worker-inspection" => {
2427        options.per_isolate.per_env.experimental_worker_inspection = value
2428      }
2429      "--heap-prof" => options.per_isolate.per_env.heap_prof = value,
2430      "--insecure-http-parser" => {
2431        options.per_isolate.per_env.insecure_http_parser = value
2432      }
2433      "[has_env_file_string]" => {
2434        options.per_isolate.per_env.has_env_file_string = value
2435      }
2436      "--experimental-default-config-file" => {
2437        options.per_isolate.per_env.experimental_default_config_file = value
2438      }
2439      "--test" => options.per_isolate.per_env.test_runner = value,
2440      "--test-force-exit" => {
2441        options.per_isolate.per_env.test_runner_force_exit = value
2442      }
2443      "--test-update-snapshots" => {
2444        options.per_isolate.per_env.test_runner_update_snapshots = value
2445      }
2446      "--experimental-test-coverage" => {
2447        options.per_isolate.per_env.test_runner_coverage = value
2448      }
2449      "--experimental-test-module-mocks" => {
2450        options.per_isolate.per_env.test_runner_module_mocks = value
2451      }
2452      "--test-only" => options.per_isolate.per_env.test_only = value,
2453      "--test-udp-no-try-send" => {
2454        options.per_isolate.per_env.test_udp_no_try_send = value
2455      }
2456      "--throw-deprecation" => {
2457        options.per_isolate.per_env.throw_deprecation = value
2458      }
2459      "--trace-deprecation" => {
2460        options.per_isolate.per_env.trace_deprecation = value
2461      }
2462      "--trace-exit" => options.per_isolate.per_env.trace_exit = value,
2463      "--trace-sync-io" => options.per_isolate.per_env.trace_sync_io = value,
2464      "--trace-tls" => options.per_isolate.per_env.trace_tls = value,
2465      "--trace-uncaught" => options.per_isolate.per_env.trace_uncaught = value,
2466      "--trace-warnings" => options.per_isolate.per_env.trace_warnings = value,
2467      "--trace-promises" => options.per_isolate.per_env.trace_promises = value,
2468      "--trace-env" => options.per_isolate.per_env.trace_env = value,
2469      "--trace-env-js-stack" => {
2470        options.per_isolate.per_env.trace_env_js_stack = value
2471      }
2472      "--trace-env-native-stack" => {
2473        options.per_isolate.per_env.trace_env_native_stack = value
2474      }
2475      "--extra-info-on-fatal-exception" => {
2476        options.per_isolate.per_env.extra_info_on_fatal_exception = value
2477      }
2478      "--verify-base-objects" => {
2479        options.per_isolate.per_env.verify_base_objects = value
2480      }
2481      "--watch" => options.per_isolate.per_env.watch_mode = value,
2482      "--watch-preserve-output" => {
2483        options.per_isolate.per_env.watch_mode_preserve_output = value
2484      }
2485      "--check" => options.per_isolate.per_env.syntax_check_only = value,
2486      "[has_eval_string]" => {
2487        options.per_isolate.per_env.has_eval_string = value
2488      }
2489      "--print" => options.per_isolate.per_env.print_eval = value,
2490      "--experimental-strip-types" => {
2491        options.per_isolate.per_env.experimental_strip_types = value
2492      }
2493      "--experimental-transform-types" => {
2494        options.per_isolate.per_env.experimental_transform_types = value
2495      }
2496      "--interactive" => options.per_isolate.per_env.force_repl = value,
2497      "--tls-min-v1.0" => options.per_isolate.per_env.tls_min_v1_0 = value,
2498      "--tls-min-v1.1" => options.per_isolate.per_env.tls_min_v1_1 = value,
2499      "--tls-min-v1.2" => options.per_isolate.per_env.tls_min_v1_2 = value,
2500      "--tls-min-v1.3" => options.per_isolate.per_env.tls_min_v1_3 = value,
2501      "--tls-max-v1.2" => options.per_isolate.per_env.tls_max_v1_2 = value,
2502      "--tls-max-v1.3" => options.per_isolate.per_env.tls_max_v1_3 = value,
2503      "--report-exclude-env" => {
2504        options.per_isolate.per_env.report_exclude_env = value
2505      }
2506      "--report-exclude-network" => {
2507        options.per_isolate.per_env.report_exclude_network = value
2508      }
2509
2510      // Per-isolate options
2511      "--track-heap-objects" => options.per_isolate.track_heap_objects = value,
2512      "--report-uncaught-exception" => {
2513        options.per_isolate.report_uncaught_exception = value
2514      }
2515      "--report-on-signal" => options.per_isolate.report_on_signal = value,
2516      "--experimental-shadow-realm" => {
2517        options.per_isolate.experimental_shadow_realm = value
2518      }
2519      "--build-snapshot" => options.per_isolate.build_snapshot = value,
2520
2521      // Per-process options
2522      "--zero-fill-buffers" => options.zero_fill_all_buffers = value,
2523      "--debug-arraybuffer-allocations" => {
2524        options.debug_arraybuffer_allocations = value
2525      }
2526      "--node-snapshot" => options.node_snapshot = value,
2527      "--completion-bash" => options.print_bash_completion = value,
2528      "--help" => options.print_help = value,
2529      "--version" => options.print_version = value,
2530      "--v8-options" => options.print_v8_help = value,
2531      "--report-compact" => options.report_compact = value,
2532      "--report-on-fatalerror" => options.report_on_fatalerror = value,
2533      "--use-openssl-ca" => options.use_openssl_ca = value,
2534      "--use-system-ca" => options.use_system_ca = value,
2535      "--use-bundled-ca" => options.use_bundled_ca = value,
2536      "[ssl_openssl_cert_store]" => options.ssl_openssl_cert_store = value,
2537      "--enable-fips" => options.enable_fips_crypto = value,
2538      "--force-fips" => options.force_fips_crypto = value,
2539      "--openssl-legacy-provider" => options.openssl_legacy_provider = value,
2540      "--openssl-shared-config" => options.openssl_shared_config = value,
2541      "--disable-wasm-trap-handler" => {
2542        options.disable_wasm_trap_handler = value
2543      }
2544      "--trace-sigint" => options.trace_sigint = value,
2545
2546      _ => {
2547        // Unknown boolean option - this is OK, might be a V8 option
2548      }
2549    }
2550  }
2551
2552  fn set_integer_field(
2553    &self,
2554    options: &mut PerProcessOptions,
2555    name: &str,
2556    value: i64,
2557  ) {
2558    match name {
2559      "--heapsnapshot-near-heap-limit" => {
2560        options.per_isolate.per_env.heap_snapshot_near_heap_limit = value
2561      }
2562      "--stack-trace-limit" => options.per_isolate.stack_trace_limit = value,
2563      "--v8-pool-size" => options.v8_thread_pool_size = value,
2564      "--secure-heap" => options.secure_heap = value,
2565      "--secure-heap-min" => options.secure_heap_min = value,
2566      _ => {
2567        // Unknown integer option
2568      }
2569    }
2570  }
2571
2572  fn set_uinteger_field(
2573    &self,
2574    options: &mut PerProcessOptions,
2575    name: &str,
2576    value: u64,
2577  ) {
2578    match name {
2579      "--network-family-autoselection-attempt-timeout" => {
2580        options
2581          .per_isolate
2582          .per_env
2583          .network_family_autoselection_attempt_timeout = value
2584      }
2585      "--max-http-header-size" => {
2586        options.per_isolate.per_env.max_http_header_size = value
2587      }
2588      "--cpu-prof-interval" => {
2589        options.per_isolate.per_env.cpu_prof_interval = value
2590      }
2591      "--heap-prof-interval" => {
2592        options.per_isolate.per_env.heap_prof_interval = value
2593      }
2594      "--test-concurrency" => {
2595        options.per_isolate.per_env.test_runner_concurrency = value
2596      }
2597      "--test-timeout" => {
2598        options.per_isolate.per_env.test_runner_timeout = value
2599      }
2600      "--test-coverage-branches" => {
2601        options.per_isolate.per_env.test_coverage_branches = value
2602      }
2603      "--test-coverage-functions" => {
2604        options.per_isolate.per_env.test_coverage_functions = value
2605      }
2606      "--test-coverage-lines" => {
2607        options.per_isolate.per_env.test_coverage_lines = value
2608      }
2609      _ => {
2610        // Unknown unsigned integer option
2611      }
2612    }
2613  }
2614
2615  fn set_string_field(
2616    &self,
2617    options: &mut PerProcessOptions,
2618    name: &str,
2619    value: String,
2620  ) {
2621    match name {
2622      // Debug options
2623      "--inspect-publish-uid" => {
2624        options
2625          .per_isolate
2626          .per_env
2627          .debug_options
2628          .inspect_publish_uid_string = value
2629      }
2630
2631      // Environment options
2632      "--dns-result-order" => {
2633        options.per_isolate.per_env.dns_result_order = value
2634      }
2635      "--diagnostic-dir" => options.per_isolate.per_env.diagnostic_dir = value,
2636      "--localstorage-file" => {
2637        options.per_isolate.per_env.localstorage_file = value
2638      }
2639      "--input-type" => options.per_isolate.per_env.input_type = value,
2640      "--heapsnapshot-signal" => {
2641        options.per_isolate.per_env.heapsnapshot_signal = value
2642      }
2643      "--cpu-prof-name" => options.per_isolate.per_env.cpu_prof_name = value,
2644      "--cpu-prof-dir" => options.per_isolate.per_env.cpu_prof_dir = value,
2645      "--heap-prof-name" => options.per_isolate.per_env.heap_prof_name = value,
2646      "--heap-prof-dir" => options.per_isolate.per_env.heap_prof_dir = value,
2647      "--redirect-warnings" => {
2648        options.per_isolate.per_env.redirect_warnings = value
2649      }
2650      "--env-file" => options.per_isolate.per_env.env_file = value,
2651      "--env-file-if-exists" => {
2652        options.per_isolate.per_env.optional_env_file = value
2653      }
2654      "--experimental-config-file" => {
2655        options.per_isolate.per_env.experimental_config_file_path = value
2656      }
2657      "--test-isolation" => options.per_isolate.per_env.test_isolation = value,
2658      "--test-global-setup" => {
2659        options.per_isolate.per_env.test_global_setup_path = value
2660      }
2661      "--test-shard" => options.per_isolate.per_env.test_shard = value,
2662      "--trace-require-module" => {
2663        options.per_isolate.per_env.trace_require_module = value
2664      }
2665      "--unhandled-rejections" => {
2666        options.per_isolate.per_env.unhandled_rejections = value
2667      }
2668      "--watch-kill-signal" => {
2669        options.per_isolate.per_env.watch_mode_kill_signal = value
2670      }
2671      "--eval" => options.per_isolate.per_env.eval_string = value,
2672      "--tls-keylog" => options.per_isolate.per_env.tls_keylog = value,
2673
2674      // Per-isolate options
2675      "--report-signal" => options.per_isolate.report_signal = value,
2676      "--build-snapshot-config" => {
2677        options.per_isolate.build_snapshot_config = value
2678      }
2679
2680      // Per-process options
2681      "--title" => options.title = value,
2682      "--trace-event-categories" => options.trace_event_categories = value,
2683      "--trace-event-file-pattern" => options.trace_event_file_pattern = value,
2684      "--disable-proto" => options.disable_proto = value,
2685      "--snapshot-blob" => options.snapshot_blob = value,
2686      "--experimental-sea-config" => options.experimental_sea_config = value,
2687      "--run" => options.run = value,
2688      "--icu-data-dir" => options.icu_data_dir = value,
2689      "--openssl-config" => options.openssl_config = value,
2690      "--tls-cipher-list" => options.tls_cipher_list = value,
2691      "--report-dir" => options.report_directory = value,
2692      "--report-filename" => options.report_filename = value,
2693      "--use-largepages" => options.use_largepages = value,
2694
2695      _ => {
2696        // Unknown string option
2697      }
2698    }
2699  }
2700
2701  fn add_to_string_list_field(
2702    &self,
2703    options: &mut PerProcessOptions,
2704    name: &str,
2705    value: String,
2706  ) {
2707    match name {
2708      "--conditions" => options.per_isolate.per_env.conditions.push(value),
2709      "--allow-fs-read" => {
2710        options.per_isolate.per_env.allow_fs_read.push(value)
2711      }
2712      "--allow-fs-write" => {
2713        options.per_isolate.per_env.allow_fs_write.push(value)
2714      }
2715      "--allow-net" => options.per_isolate.per_env.allow_net.push(value),
2716      "--experimental-loader" => {
2717        options.per_isolate.per_env.userland_loaders.push(value)
2718      }
2719      "--disable-warning" => {
2720        options.per_isolate.per_env.disable_warnings.push(value)
2721      }
2722      "--test-name-pattern" => {
2723        options.per_isolate.per_env.test_name_pattern.push(value)
2724      }
2725      "--test-reporter" => {
2726        options.per_isolate.per_env.test_reporter.push(value)
2727      }
2728      "--test-reporter-destination" => options
2729        .per_isolate
2730        .per_env
2731        .test_reporter_destination
2732        .push(value),
2733      "--test-skip-pattern" => {
2734        options.per_isolate.per_env.test_skip_pattern.push(value)
2735      }
2736      "--test-coverage-include" => options
2737        .per_isolate
2738        .per_env
2739        .coverage_include_pattern
2740        .push(value),
2741      "--test-coverage-exclude" => options
2742        .per_isolate
2743        .per_env
2744        .coverage_exclude_pattern
2745        .push(value),
2746      "--watch-path" => {
2747        options.per_isolate.per_env.watch_mode_paths.push(value)
2748      }
2749      "--require" => {
2750        options.per_isolate.per_env.preload_cjs_modules.push(value)
2751      }
2752      "--import" => options.per_isolate.per_env.preload_esm_modules.push(value),
2753      "--security-revert" => options.security_reverts.push(value),
2754      _ => {
2755        // Unknown string list option
2756      }
2757    }
2758  }
2759
2760  fn set_host_port_field(
2761    &self,
2762    options: &mut PerProcessOptions,
2763    name: &str,
2764    value: HostPort,
2765  ) {
2766    match name {
2767      "--inspect-port" => options
2768        .per_isolate
2769        .per_env
2770        .debug_options
2771        .host_port
2772        .update(&value),
2773      _ => {
2774        // Unknown host port option
2775      }
2776    }
2777  }
2778
2779  pub fn parse(&self, args: Vec<String>) -> Result<ParseResult, Vec<String>> {
2780    let mut args = args;
2781
2782    let mut v8_args = Vec::new();
2783    let mut errors = Vec::new();
2784    let mut options = PerProcessOptions::default();
2785    let mut synthetic_args = Vec::new();
2786
2787    // The args does not contain the executable name, so we do not need to skip it.
2788    let mut i = 0;
2789
2790    while i < args.len() + synthetic_args.len() && errors.is_empty() {
2791      let arg = if !synthetic_args.is_empty() {
2792        synthetic_args.remove(0)
2793      } else {
2794        if i >= args.len() {
2795          break;
2796        }
2797        let arg = args[i].clone();
2798        i += 1;
2799        arg
2800      };
2801
2802      if arg.len() <= 1 || !arg.starts_with('-') {
2803        // Not an option, stop processing
2804        if synthetic_args.is_empty() {
2805          i -= 1; // Put it back
2806        }
2807        break;
2808      }
2809
2810      if arg == "--" {
2811        break;
2812      }
2813
2814      let (name, value, has_equals) = if arg.starts_with("--") {
2815        if let Some(eq_pos) = arg.find('=') {
2816          (
2817            arg[..eq_pos].to_string(),
2818            Some(arg[eq_pos + 1..].to_string()),
2819            true,
2820          )
2821        } else {
2822          (arg.clone(), None, false)
2823        }
2824      } else {
2825        (arg.clone(), None, false)
2826      };
2827
2828      let original_name = if has_equals {
2829        name.clone() + "="
2830      } else {
2831        name.clone()
2832      };
2833
2834      // Normalize underscores to dashes
2835      let mut normalized_name = name.clone();
2836      if normalized_name.starts_with("--") {
2837        normalized_name = normalized_name.replace('_', "-");
2838      }
2839
2840      // Handle negation
2841      let (is_negation, final_name) =
2842        if let Some(stripped) = normalized_name.strip_prefix("--no-") {
2843          (true, "--".to_string() + stripped)
2844        } else {
2845          (false, normalized_name)
2846        };
2847
2848      // Expand aliases
2849      let mut current_name = final_name.clone();
2850      let mut expansion_count = 0;
2851      while expansion_count < 10 && current_name != "--" {
2852        if let Some(alias_expansion) = self.aliases.get(&current_name) {
2853          if !alias_expansion.is_empty() {
2854            let new_name = alias_expansion[0].clone();
2855            // Stop if alias expands to itself (e.g., --prof-process -> [--prof-process, --])
2856            if new_name == current_name {
2857              if alias_expansion.len() > 1 {
2858                for item in alias_expansion[1..].iter().rev() {
2859                  synthetic_args.insert(0, item.clone());
2860                }
2861              }
2862              break;
2863            }
2864            current_name = new_name;
2865            if alias_expansion.len() > 1 {
2866              for item in alias_expansion[1..].iter().rev() {
2867                synthetic_args.insert(0, item.clone());
2868              }
2869            }
2870            expansion_count += 1;
2871          } else {
2872            break;
2873          }
2874        } else if has_equals {
2875          if let Some(alias_expansion) =
2876            self.aliases.get(&(current_name.clone() + "="))
2877          {
2878            if !alias_expansion.is_empty() {
2879              let new_name = alias_expansion[0].clone();
2880              // Stop if alias expands to itself
2881              if new_name == current_name {
2882                if alias_expansion.len() > 1 {
2883                  for item in alias_expansion[1..].iter().rev() {
2884                    synthetic_args.insert(0, item.clone());
2885                  }
2886                }
2887                break;
2888              }
2889              current_name = new_name;
2890              if alias_expansion.len() > 1 {
2891                for item in alias_expansion[1..].iter().rev() {
2892                  synthetic_args.insert(0, item.clone());
2893                }
2894              }
2895              expansion_count += 1;
2896            } else {
2897              break;
2898            }
2899          } else {
2900            break;
2901          }
2902        } else {
2903          break;
2904        }
2905      }
2906
2907      // Handle implications
2908      let implied_name = if is_negation {
2909        "--no-".to_string() + &current_name[2..]
2910      } else {
2911        current_name.clone()
2912      };
2913
2914      if let Some(implications) = self.implications.get(&implied_name) {
2915        for implication in implications {
2916          if implication.starts_with("--") {
2917            if let Some(stripped) = implication.strip_prefix("--no-") {
2918              let target_name = "--".to_string() + stripped;
2919              if let Some(option_info) = self.options.get(&target_name)
2920                && option_info.option_type == OptionType::Boolean
2921              {
2922                self.set_boolean_field(&mut options, &target_name, false);
2923              }
2924            } else {
2925              // Check if it's a boolean option we handle
2926              if let Some(option_info) = self.options.get(implication) {
2927                if option_info.option_type == OptionType::Boolean {
2928                  self.set_boolean_field(&mut options, implication, true);
2929                } else {
2930                  v8_args.push(implication.clone());
2931                }
2932              } else {
2933                v8_args.push(implication.clone());
2934              }
2935            }
2936          } else {
2937            // Handle special implications like [has_eval_string]
2938            if let Some(option_info) = self.options.get(implication)
2939              && option_info.option_type == OptionType::Boolean
2940            {
2941              self.set_boolean_field(&mut options, implication, true);
2942            }
2943          }
2944        }
2945      }
2946
2947      // Check if option exists
2948      if let Some(option_info) = self.options.get(&current_name) {
2949        // Validate negation
2950        if is_negation
2951          && option_info.option_type != OptionType::Boolean
2952          && option_info.option_type != OptionType::V8Option
2953        {
2954          errors.push(format!(
2955            "{} is an invalid negation because it is not a boolean option",
2956            arg
2957          ));
2958          break;
2959        }
2960
2961        // Get value for non-boolean options
2962        let option_value = if matches!(
2963          option_info.option_type,
2964          OptionType::Boolean | OptionType::NoOp | OptionType::V8Option
2965        ) {
2966          String::new()
2967        } else if let Some(val) = value {
2968          if val.is_empty() {
2969            errors.push(format!("{} requires an argument", original_name));
2970            break;
2971          }
2972          val
2973        } else {
2974          // Need to get next argument
2975          let next_val = if !synthetic_args.is_empty() {
2976            synthetic_args.remove(0)
2977          } else if i < args.len() {
2978            let val = args[i].clone();
2979            i += 1;
2980            val
2981          } else {
2982            errors.push(format!("{} requires an argument", original_name));
2983            break;
2984          };
2985
2986          if next_val.starts_with('-')
2987            && (next_val.len() == 1
2988              || !next_val[1..].chars().all(|c| c.is_ascii_digit()))
2989          {
2990            errors.push(format!("{} requires an argument", original_name));
2991            break;
2992          }
2993
2994          // Handle escaped dash
2995          if next_val.starts_with("\\-") {
2996            next_val[1..].to_string()
2997          } else {
2998            next_val
2999          }
3000        };
3001
3002        // Apply option
3003        match option_info.option_type {
3004          OptionType::V8Option => {
3005            v8_args.push(arg);
3006          }
3007          _ => {
3008            self.apply_option_value(
3009              &mut options,
3010              &current_name,
3011              &option_value,
3012              is_negation,
3013              option_info,
3014              &mut errors,
3015            );
3016          }
3017        }
3018      } else {
3019        // Unknown option, pass to V8
3020        v8_args.push(arg);
3021      }
3022    }
3023
3024    // Remove processed arguments from original args array
3025    args.drain(0..i);
3026    args.splice(0..0, synthetic_args);
3027
3028    // Watch mode validation - check if --watch is used without files and not in test mode
3029    if options.per_isolate.per_env.watch_mode
3030      && !options.per_isolate.per_env.test_runner
3031      && args.is_empty()
3032    {
3033      errors.push("--watch requires specifying a file".to_string());
3034    }
3035
3036    // Run option validation
3037    options.check_options(&mut errors);
3038
3039    if errors.is_empty() {
3040      Ok(ParseResult {
3041        options,
3042        remaining_args: args,
3043        v8_args,
3044      })
3045    } else {
3046      Err(errors)
3047    }
3048  }
3049}
3050
3051#[derive(Debug, Clone)]
3052pub struct ParseResult {
3053  pub options: PerProcessOptions,
3054  pub remaining_args: Vec<String>,
3055  pub v8_args: Vec<String>,
3056}
3057
3058/// Parse NODE_OPTIONS environment variable
3059pub fn parse_node_options_env_var(
3060  node_options: &str,
3061) -> Result<Vec<String>, Vec<String>> {
3062  let mut env_argv = Vec::new();
3063  let mut errors = Vec::new();
3064  let mut is_in_string = false;
3065  let mut will_start_new_arg = true;
3066
3067  let chars: Vec<char> = node_options.chars().collect();
3068  let mut index = 0;
3069
3070  while index < chars.len() {
3071    let mut c = chars[index];
3072
3073    // Backslashes escape the following character
3074    if c == '\\' && is_in_string {
3075      if index + 1 == chars.len() {
3076        errors
3077          .push("invalid value for NODE_OPTIONS (invalid escape)".to_string());
3078        return Err(errors);
3079      } else {
3080        index += 1;
3081        c = chars[index];
3082      }
3083    } else if c == ' ' && !is_in_string {
3084      will_start_new_arg = true;
3085      index += 1;
3086      continue;
3087    } else if c == '"' {
3088      is_in_string = !is_in_string;
3089      index += 1;
3090      continue;
3091    }
3092
3093    if will_start_new_arg {
3094      env_argv.push(c.to_string());
3095      will_start_new_arg = false;
3096    } else if let Some(last) = env_argv.last_mut() {
3097      last.push(c);
3098    }
3099
3100    index += 1;
3101  }
3102
3103  if is_in_string {
3104    errors
3105      .push("invalid value for NODE_OPTIONS (unterminated string)".to_string());
3106    return Err(errors);
3107  }
3108
3109  Ok(env_argv)
3110}
3111
3112/// Main parsing function
3113pub fn parse_args(args: Vec<String>) -> Result<ParseResult, Vec<String>> {
3114  let parser = OptionsParser::new();
3115  parser.parse(args)
3116}
3117
3118/// Options for controlling translation behavior
3119#[derive(Debug, Clone, Default)]
3120pub struct TranslateOptions {
3121  /// Use "deno node" as base command (for standalone CLI)
3122  /// When false, uses "deno run" (for child_process spawning)
3123  pub use_node_subcommand: bool,
3124  /// Add unstable Node.js compat flags (--unstable-node-globals,
3125  /// --unstable-bare-node-builtins, --unstable-detect-cjs)
3126  pub add_unstable_flags: bool,
3127  /// Add standalone config overrides (--node-modules-dir=manual, --no-config)
3128  /// Only appropriate for the standalone node shim CLI, not for child processes
3129  pub add_standalone_config: bool,
3130  /// Wrap eval code for Node.js compatibility (builtin modules as globals)
3131  pub wrap_eval_code: bool,
3132}
3133
3134impl TranslateOptions {
3135  /// Options for standalone node shim CLI
3136  pub fn for_node_cli() -> Self {
3137    Self {
3138      use_node_subcommand: true,
3139      add_unstable_flags: true,
3140      add_standalone_config: true,
3141      wrap_eval_code: false,
3142    }
3143  }
3144
3145  /// Options for child_process spawning within Deno
3146  pub fn for_child_process() -> Self {
3147    Self {
3148      use_node_subcommand: false,
3149      add_unstable_flags: true,
3150      add_standalone_config: false,
3151      wrap_eval_code: true,
3152    }
3153  }
3154
3155  /// Options for transforming commands embedded in shell strings.
3156  /// Like child_process but without eval wrapping, since the shell
3157  /// handles quoting and wrapping would introduce metacharacters.
3158  pub fn for_shell_command() -> Self {
3159    Self {
3160      use_node_subcommand: false,
3161      add_unstable_flags: true,
3162      add_standalone_config: false,
3163      wrap_eval_code: false,
3164    }
3165  }
3166}
3167
3168/// Result of translating Node.js CLI args to Deno args
3169#[derive(Debug, Clone, Default)]
3170pub struct TranslatedArgs {
3171  /// The Deno CLI arguments
3172  pub deno_args: Vec<String>,
3173  /// Node options that should be added to NODE_OPTIONS env var
3174  pub node_options: Vec<String>,
3175  /// Whether to set DENO_TLS_CA_STORE=system
3176  pub use_system_ca: bool,
3177}
3178
3179/// Wraps eval code for Node.js compatibility.
3180/// Makes builtin modules available as global variables.
3181pub fn wrap_eval_code(source_code: &str) -> String {
3182  // Use serde_json to properly escape the source code
3183  let json_escaped = serde_json::to_string(source_code).unwrap_or_else(|_| {
3184    // Fallback: basic escaping
3185    format!(
3186      "\"{}\"",
3187      source_code.replace('\\', "\\\\").replace('"', "\\\"")
3188    )
3189  });
3190
3191  format!(
3192    r#"(
3193    globalThis.require = process.getBuiltinModule("module").createRequire(import.meta.url),
3194    process.getBuiltinModule("module").builtinModules
3195      .filter((m) => !/\/|crypto|process/.test(m))
3196      .forEach((m) => {{ globalThis[m] = process.getBuiltinModule(m); }}),
3197    process.getBuiltinModule("vm").runInThisContext({})
3198  )"#,
3199    json_escaped
3200  )
3201}
3202
3203/// Deno subcommands - if the first arg is one of these, pass through unchanged
3204const DENO_SUBCOMMANDS: &[&str] = &[
3205  "add",
3206  "bench",
3207  "cache",
3208  "check",
3209  "compile",
3210  "completions",
3211  "coverage",
3212  "doc",
3213  "eval",
3214  "fmt",
3215  "help",
3216  "info",
3217  "init",
3218  "install",
3219  "lint",
3220  "lsp",
3221  "publish",
3222  "repl",
3223  "run",
3224  "task",
3225  "tasks",
3226  "test",
3227  "types",
3228  "uninstall",
3229  "upgrade",
3230  "vendor",
3231];
3232
3233/// Check if a string is a Deno subcommand
3234pub fn is_deno_subcommand(arg: &str) -> bool {
3235  DENO_SUBCOMMANDS.contains(&arg)
3236}
3237
3238/// Translate parsed Node.js CLI arguments to Deno CLI arguments.
3239pub fn translate_to_deno_args(
3240  parsed_args: ParseResult,
3241  options: &TranslateOptions,
3242) -> TranslatedArgs {
3243  let mut result = TranslatedArgs::default();
3244  let deno_args = &mut result.deno_args;
3245  let node_options = &mut result.node_options;
3246
3247  // Check if the args already look like Deno args (e.g., from vitest workers)
3248  // If the first remaining arg is a Deno subcommand, pass through unchanged
3249  if let Some(first_arg) = parsed_args.remaining_args.first()
3250    && is_deno_subcommand(first_arg)
3251  {
3252    // Already Deno-style args, return unchanged
3253    result.deno_args = parsed_args.remaining_args;
3254    return result;
3255  }
3256
3257  let opts = &parsed_args.options;
3258  let env_opts = &opts.per_isolate.per_env;
3259
3260  // Check for system CA usage
3261  if opts.use_system_ca || opts.use_openssl_ca {
3262    result.use_system_ca = true;
3263  }
3264
3265  // Handle --version flag
3266  if opts.print_version {
3267    if options.use_node_subcommand {
3268      deno_args.push("node".to_string());
3269    }
3270    deno_args.push("--version".to_string());
3271    return result;
3272  }
3273
3274  // Handle --v8-options flag (print V8 help and exit)
3275  if opts.print_v8_help {
3276    if options.use_node_subcommand {
3277      deno_args.push("node".to_string());
3278      deno_args.push("run".to_string());
3279    }
3280    deno_args.push("--v8-flags=--help".to_string());
3281    return result;
3282  }
3283
3284  // Handle --help flag
3285  if opts.print_help {
3286    if options.use_node_subcommand {
3287      // For CLI, we handle help specially
3288      deno_args.push("node".to_string());
3289    }
3290    deno_args.push("--help".to_string());
3291    return result;
3292  }
3293
3294  // Handle --completion-bash flag (translate to Deno completions)
3295  if opts.print_bash_completion {
3296    deno_args.push("completions".to_string());
3297    deno_args.push("bash".to_string());
3298    return result;
3299  }
3300
3301  // Handle --run flag (run package.json script via deno task)
3302  if !opts.run.is_empty() {
3303    if options.use_node_subcommand {
3304      deno_args.push("node".to_string());
3305    }
3306    deno_args.push("task".to_string());
3307    deno_args.push(opts.run.clone());
3308    deno_args.extend(parsed_args.remaining_args);
3309    return result;
3310  }
3311
3312  // Handle -e/--eval or -p/--print
3313  // Note: -p/--print alone (without -e) uses the first remaining arg as eval code
3314  let eval_string_for_print = if !env_opts.has_eval_string
3315    && env_opts.print_eval
3316    && !parsed_args.remaining_args.is_empty()
3317  {
3318    Some(parsed_args.remaining_args[0].clone())
3319  } else {
3320    None
3321  };
3322
3323  if env_opts.has_eval_string || eval_string_for_print.is_some() {
3324    if options.use_node_subcommand {
3325      deno_args.push("node".to_string());
3326    }
3327    deno_args.push("eval".to_string());
3328    // Note: deno eval has implicit permissions, so we don't add -A
3329
3330    if options.add_unstable_flags {
3331      deno_args.push("--unstable-node-globals".to_string());
3332      deno_args.push("--unstable-bare-node-builtins".to_string());
3333      deno_args.push("--unstable-detect-cjs".to_string());
3334    }
3335    if options.add_standalone_config {
3336      deno_args.push("--node-modules-dir=manual".to_string());
3337      deno_args.push("--no-config".to_string());
3338    }
3339
3340    if env_opts.has_env_file_string {
3341      if env_opts.env_file.is_empty() {
3342        deno_args.push("--env-file".to_string());
3343      } else {
3344        deno_args.push(format!("--env-file={}", env_opts.env_file));
3345      }
3346    }
3347
3348    if env_opts.print_eval {
3349      deno_args.push("--print".to_string());
3350    }
3351
3352    if !parsed_args.v8_args.is_empty() {
3353      deno_args.push(format!("--v8-flags={}", parsed_args.v8_args.join(",")));
3354    }
3355
3356    // Add conditions and inspector flags for eval
3357    add_conditions(deno_args, env_opts);
3358    add_inspector_flags(deno_args, env_opts);
3359
3360    // Get the eval code from either the explicit eval_string or the first remaining arg (for -p)
3361    let raw_eval_code = eval_string_for_print
3362      .as_ref()
3363      .unwrap_or(&env_opts.eval_string);
3364    let eval_code = if options.wrap_eval_code {
3365      wrap_eval_code(raw_eval_code)
3366    } else {
3367      raw_eval_code.clone()
3368    };
3369    deno_args.push(eval_code);
3370
3371    if options.use_node_subcommand {
3372      deno_args.push("--".to_string());
3373    }
3374    // For -p with first arg as eval code, skip that arg
3375    let remaining_args = if eval_string_for_print.is_some() {
3376      parsed_args.remaining_args[1..].to_vec()
3377    } else {
3378      parsed_args.remaining_args
3379    };
3380    deno_args.extend(remaining_args);
3381    return result;
3382  }
3383
3384  // Handle --test flag (run tests via deno test)
3385  if env_opts.test_runner {
3386    if options.use_node_subcommand {
3387      deno_args.push("node".to_string());
3388    }
3389    deno_args.push("test".to_string());
3390    deno_args.push("-A".to_string());
3391
3392    if options.add_unstable_flags {
3393      deno_args.push("--unstable-node-globals".to_string());
3394      deno_args.push("--unstable-bare-node-builtins".to_string());
3395      deno_args.push("--unstable-detect-cjs".to_string());
3396    }
3397    if options.add_standalone_config {
3398      deno_args.push("--node-modules-dir=manual".to_string());
3399      deno_args.push("--no-config".to_string());
3400    }
3401
3402    add_common_flags(deno_args, &parsed_args, env_opts);
3403    deno_args.extend(parsed_args.remaining_args);
3404    return result;
3405  }
3406
3407  // Handle REPL (no arguments or force_repl)
3408  if parsed_args.remaining_args.is_empty() || env_opts.force_repl {
3409    if options.use_node_subcommand {
3410      deno_args.push("node".to_string());
3411      deno_args.push("repl".to_string());
3412      deno_args.push("-A".to_string());
3413
3414      if !parsed_args.v8_args.is_empty() {
3415        deno_args.push(format!("--v8-flags={}", parsed_args.v8_args.join(",")));
3416      }
3417
3418      add_conditions(deno_args, env_opts);
3419      add_inspector_flags(deno_args, env_opts);
3420
3421      deno_args.push("--".to_string());
3422      deno_args.extend(parsed_args.remaining_args);
3423    } else {
3424      // For child_process, return empty args to trigger REPL behavior
3425      if !parsed_args.v8_args.is_empty() {
3426        deno_args.push(format!("--v8-flags={}", parsed_args.v8_args.join(",")));
3427      }
3428    }
3429    return result;
3430  }
3431
3432  // Handle running a script
3433  if options.use_node_subcommand {
3434    deno_args.push("node".to_string());
3435  }
3436  deno_args.push("run".to_string());
3437  deno_args.push("-A".to_string());
3438
3439  if options.add_unstable_flags {
3440    deno_args.push("--unstable-node-globals".to_string());
3441    deno_args.push("--unstable-bare-node-builtins".to_string());
3442    deno_args.push("--unstable-detect-cjs".to_string());
3443  }
3444  if options.add_standalone_config {
3445    deno_args.push("--node-modules-dir=manual".to_string());
3446    deno_args.push("--no-config".to_string());
3447  }
3448
3449  add_common_flags(deno_args, &parsed_args, env_opts);
3450
3451  // Handle --no-warnings -> --quiet
3452  if !env_opts.warnings {
3453    deno_args.push("--quiet".to_string());
3454    node_options.push("--no-warnings".to_string());
3455  }
3456
3457  // Handle --pending-deprecation (pass to NODE_OPTIONS)
3458  if env_opts.pending_deprecation {
3459    node_options.push("--pending-deprecation".to_string());
3460  }
3461
3462  // Add the script and remaining args
3463  deno_args.extend(parsed_args.remaining_args);
3464
3465  result
3466}
3467
3468fn add_common_flags(
3469  deno_args: &mut Vec<String>,
3470  parsed_args: &ParseResult,
3471  env_opts: &EnvironmentOptions,
3472) {
3473  // Add watch mode if enabled
3474  if env_opts.watch_mode {
3475    if env_opts.watch_mode_paths.is_empty() {
3476      deno_args.push("--watch".to_string());
3477    } else {
3478      deno_args.push(format!(
3479        "--watch={}",
3480        env_opts
3481          .watch_mode_paths
3482          .iter()
3483          .map(|p| p.replace(',', ",,"))
3484          .collect::<Vec<String>>()
3485          .join(",")
3486      ));
3487    }
3488  }
3489
3490  // Add env file if specified
3491  if env_opts.has_env_file_string {
3492    if env_opts.env_file.is_empty() {
3493      deno_args.push("--env-file".to_string());
3494    } else {
3495      deno_args.push(format!("--env-file={}", env_opts.env_file));
3496    }
3497  }
3498
3499  // Add V8 flags
3500  if !parsed_args.v8_args.is_empty() {
3501    deno_args.push(format!("--v8-flags={}", parsed_args.v8_args.join(",")));
3502  }
3503
3504  // Add conditions
3505  add_conditions(deno_args, env_opts);
3506
3507  // Add inspector flags
3508  add_inspector_flags(deno_args, env_opts);
3509}
3510
3511fn add_conditions(deno_args: &mut Vec<String>, env_opts: &EnvironmentOptions) {
3512  if !env_opts.conditions.is_empty() {
3513    for condition in &env_opts.conditions {
3514      deno_args.push(format!("--conditions={}", condition));
3515    }
3516  }
3517}
3518
3519fn add_inspector_flags(
3520  deno_args: &mut Vec<String>,
3521  env_opts: &EnvironmentOptions,
3522) {
3523  if env_opts.debug_options.inspector_enabled {
3524    let arg = if env_opts.debug_options.break_first_line {
3525      "--inspect-brk"
3526    } else if env_opts.debug_options.inspect_wait {
3527      "--inspect-wait"
3528    } else {
3529      "--inspect"
3530    };
3531    deno_args.push(format!(
3532      "{}={}:{}",
3533      arg,
3534      env_opts.debug_options.host_port.host,
3535      env_opts.debug_options.host_port.port
3536    ));
3537    deno_args.push(format!(
3538      "--inspect-publish-uid={}",
3539      env_opts.debug_options.inspect_publish_uid_string
3540    ));
3541  }
3542}
3543
3544#[cfg(test)]
3545mod tests {
3546  use super::*;
3547
3548  /// Macro to create a Vec<String> from string literals
3549  macro_rules! svec {
3550        ($($x:expr),* $(,)?) => {
3551            vec![$($x.to_string()),*]
3552        };
3553    }
3554
3555  #[test]
3556  fn test_basic_parsing() {
3557    let result = parse_args(svec!["--version"]).unwrap();
3558    assert!(result.options.print_version);
3559  }
3560
3561  #[test]
3562  fn test_help_parsing() {
3563    let result = parse_args(svec!["--help"]).unwrap();
3564    assert!(result.options.print_help);
3565  }
3566
3567  #[test]
3568  fn test_debug_options() {
3569    let result = parse_args(svec!["--inspect"]).unwrap();
3570    assert!(
3571      result
3572        .options
3573        .per_isolate
3574        .per_env
3575        .debug_options
3576        .inspector_enabled
3577    );
3578  }
3579
3580  #[test]
3581  fn test_string_option() {
3582    let result = parse_args(svec!["--title", "myapp"]).unwrap();
3583    assert_eq!(result.options.title, "myapp");
3584  }
3585
3586  #[test]
3587  fn test_boolean_negation() {
3588    let result = parse_args(svec!["--no-warnings"]).unwrap();
3589    assert!(!result.options.per_isolate.per_env.warnings);
3590  }
3591
3592  #[test]
3593  fn test_alias_expansion() {
3594    let result = parse_args(svec!["-v"]).unwrap();
3595    assert!(result.options.print_version);
3596  }
3597
3598  #[test]
3599  fn test_node_options_parsing() {
3600    let env_args =
3601      parse_node_options_env_var("--inspect --title \"my app\"").unwrap();
3602    assert_eq!(env_args, vec!["--inspect", "--title", "my app"]);
3603  }
3604
3605  #[test]
3606  fn test_host_port_parsing() {
3607    let result = parse_args(svec!["--inspect-port", "127.0.0.1:9229"]).unwrap();
3608    assert_eq!(
3609      result
3610        .options
3611        .per_isolate
3612        .per_env
3613        .debug_options
3614        .host_port
3615        .host,
3616      "127.0.0.1"
3617    );
3618    assert_eq!(
3619      result
3620        .options
3621        .per_isolate
3622        .per_env
3623        .debug_options
3624        .host_port
3625        .port,
3626      9229
3627    );
3628  }
3629
3630  // Tests for incompatible argument combinations
3631  #[test]
3632  fn test_check_eval_incompatible() {
3633    let result = parse_args(svec!["--check", "--eval", "console.log(42)"]);
3634    assert!(result.is_err());
3635    let errors = result.unwrap_err();
3636    assert!(
3637      errors
3638        .iter()
3639        .any(|e| e.contains("either --check or --eval can be used, not both"))
3640    );
3641  }
3642
3643  #[test]
3644  fn test_test_check_incompatible() {
3645    let result = parse_args(svec!["--test", "--check"]);
3646    assert!(result.is_err());
3647    let errors = result.unwrap_err();
3648    assert!(
3649      errors
3650        .iter()
3651        .any(|e| e.contains("either --test or --check can be used, not both"))
3652    );
3653  }
3654
3655  #[test]
3656  fn test_test_eval_incompatible() {
3657    let result = parse_args(svec!["--test", "--eval", "console.log(42)"]);
3658    assert!(result.is_err());
3659    let errors = result.unwrap_err();
3660    assert!(
3661      errors
3662        .iter()
3663        .any(|e| e.contains("either --test or --eval can be used, not both"))
3664    );
3665  }
3666
3667  #[test]
3668  fn test_test_interactive_incompatible() {
3669    let result = parse_args(svec!["--test", "--interactive"]);
3670    assert!(result.is_err());
3671    let errors = result.unwrap_err();
3672    assert!(errors.iter().any(|e| {
3673      e.contains("either --test or --interactive can be used, not both")
3674    }));
3675  }
3676
3677  #[test]
3678  fn test_test_watch_path_incompatible() {
3679    let result = parse_args(svec!["--test", "--watch-path", "."]);
3680    assert!(result.is_err());
3681    let errors = result.unwrap_err();
3682    assert!(errors.iter().any(|e| {
3683      e.contains("--watch-path cannot be used in combination with --test")
3684    }));
3685  }
3686
3687  #[test]
3688  fn test_watch_check_incompatible() {
3689    let result = parse_args(svec!["--watch", "--check"]);
3690    assert!(result.is_err());
3691    let errors = result.unwrap_err();
3692    assert!(
3693      errors
3694        .iter()
3695        .any(|e| e.contains("either --watch or --check can be used, not both"))
3696    );
3697  }
3698
3699  #[test]
3700  fn test_watch_eval_incompatible() {
3701    let result = parse_args(svec!["--watch", "--eval", "console.log(42)"]);
3702    assert!(result.is_err());
3703    let errors = result.unwrap_err();
3704    assert!(
3705      errors
3706        .iter()
3707        .any(|e| e.contains("either --watch or --eval can be used, not both"))
3708    );
3709  }
3710
3711  #[test]
3712  fn test_watch_interactive_incompatible() {
3713    let result = parse_args(svec!["--watch", "--interactive"]);
3714    assert!(result.is_err());
3715    let errors = result.unwrap_err();
3716    assert!(errors.iter().any(|e| {
3717      e.contains("either --watch or --interactive can be used, not both")
3718    }));
3719  }
3720
3721  #[test]
3722  fn test_watch_test_force_exit_incompatible() {
3723    let result = parse_args(svec!["--watch", "--test-force-exit"]);
3724    assert!(result.is_err());
3725    let errors = result.unwrap_err();
3726    assert!(errors.iter().any(|e| {
3727      e.contains("either --watch or --test-force-exit can be used, not both")
3728    }));
3729  }
3730
3731  #[test]
3732  fn test_tls_min_max_incompatible() {
3733    let result = parse_args(svec!["--tls-min-v1.3", "--tls-max-v1.2"]);
3734    assert!(result.is_err());
3735    let errors = result.unwrap_err();
3736    assert!(errors.iter().any(|e| e.contains(
3737      "either --tls-min-v1.3 or --tls-max-v1.2 can be used, not both"
3738    )));
3739  }
3740
3741  #[test]
3742  fn test_openssl_ca_bundled_ca_incompatible() {
3743    let result = parse_args(svec!["--use-openssl-ca", "--use-bundled-ca"]);
3744    assert!(result.is_err());
3745    let errors = result.unwrap_err();
3746    assert!(errors.iter().any(|e| {
3747      e.contains(
3748        "either --use-openssl-ca or --use-bundled-ca can be used, not both",
3749      )
3750    }));
3751  }
3752
3753  #[test]
3754  fn test_cpu_prof_name_without_cpu_prof() {
3755    let result = parse_args(svec!["--cpu-prof-name", "profile.log"]);
3756    assert!(result.is_err());
3757    let errors = result.unwrap_err();
3758    assert!(
3759      errors
3760        .iter()
3761        .any(|e| e.contains("--cpu-prof-name must be used with --cpu-prof"))
3762    );
3763  }
3764
3765  #[test]
3766  fn test_cpu_prof_dir_without_cpu_prof() {
3767    let result = parse_args(svec!["--cpu-prof-dir", "/tmp"]);
3768    assert!(result.is_err());
3769    let errors = result.unwrap_err();
3770    assert!(
3771      errors
3772        .iter()
3773        .any(|e| e.contains("--cpu-prof-dir must be used with --cpu-prof"))
3774    );
3775  }
3776
3777  #[test]
3778  fn test_cpu_prof_interval_without_cpu_prof() {
3779    let result = parse_args(svec!["--cpu-prof-interval", "500"]);
3780    assert!(result.is_err());
3781    let errors = result.unwrap_err();
3782    assert!(
3783            errors
3784                .iter()
3785                .any(|e| e.contains("--cpu-prof-interval must be used with --cpu-prof"))
3786        );
3787  }
3788
3789  #[test]
3790  fn test_heap_prof_name_without_heap_prof() {
3791    let result = parse_args(svec!["--heap-prof-name", "heap.log"]);
3792    assert!(result.is_err());
3793    let errors = result.unwrap_err();
3794    assert!(
3795      errors
3796        .iter()
3797        .any(|e| e.contains("--heap-prof-name must be used with --heap-prof"))
3798    );
3799  }
3800
3801  #[test]
3802  fn test_heap_prof_dir_without_heap_prof() {
3803    let result = parse_args(svec!["--heap-prof-dir", "/tmp"]);
3804    assert!(result.is_err());
3805    let errors = result.unwrap_err();
3806    assert!(
3807      errors
3808        .iter()
3809        .any(|e| e.contains("--heap-prof-dir must be used with --heap-prof"))
3810    );
3811  }
3812
3813  #[test]
3814  fn test_heap_prof_interval_without_heap_prof() {
3815    let result = parse_args(svec!["--heap-prof-interval", "1024"]);
3816    assert!(result.is_err());
3817    let errors = result.unwrap_err();
3818    assert!(errors.iter().any(|e| {
3819      e.contains("--heap-prof-interval must be used with --heap-prof")
3820    }));
3821  }
3822
3823  #[test]
3824  fn test_invalid_input_type() {
3825    let result = parse_args(svec!["--input-type", "invalid"]);
3826    assert!(result.is_err());
3827    let errors = result.unwrap_err();
3828    assert!(errors.iter().any(|e| e.contains("--input-type must be \"module\", \"commonjs\", \"module-typescript\" or \"commonjs-typescript\"")));
3829  }
3830
3831  #[test]
3832  fn test_invalid_unhandled_rejections() {
3833    let result = parse_args(svec!["--unhandled-rejections", "invalid"]);
3834    assert!(result.is_err());
3835    let errors = result.unwrap_err();
3836    assert!(
3837      errors
3838        .iter()
3839        .any(|e| e.contains("invalid value for --unhandled-rejections"))
3840    );
3841  }
3842
3843  #[test]
3844  fn test_invalid_trace_require_module() {
3845    let result = parse_args(svec!["--trace-require-module", "invalid"]);
3846    assert!(result.is_err());
3847    let errors = result.unwrap_err();
3848    assert!(
3849      errors
3850        .iter()
3851        .any(|e| e.contains("invalid value for --trace-require-module"))
3852    );
3853  }
3854
3855  #[test]
3856  fn test_invalid_test_isolation() {
3857    let result = parse_args(svec!["--test", "--test-isolation", "invalid"]);
3858    assert!(result.is_err());
3859    let errors = result.unwrap_err();
3860    assert!(
3861      errors
3862        .iter()
3863        .any(|e| e.contains("invalid value for --test-isolation"))
3864    );
3865  }
3866
3867  #[test]
3868  fn test_invalid_use_largepages() {
3869    let result = parse_args(svec!["--use-largepages", "invalid"]);
3870    assert!(result.is_err());
3871    let errors = result.unwrap_err();
3872    assert!(
3873      errors
3874        .iter()
3875        .any(|e| e.contains("invalid value for --use-largepages"))
3876    );
3877  }
3878
3879  #[test]
3880  fn test_negative_heapsnapshot_near_heap_limit() {
3881    let result = parse_args(svec!["--heapsnapshot-near-heap-limit", "-1"]);
3882    assert!(result.is_err());
3883    let errors = result.unwrap_err();
3884    assert!(errors.iter().any(|e| {
3885      e.contains("--heapsnapshot-near-heap-limit must not be negative")
3886    }));
3887  }
3888
3889  #[test]
3890  fn test_secure_heap_not_power_of_two() {
3891    let result = parse_args(svec!["--secure-heap", "3"]);
3892    assert!(result.is_err());
3893    let errors = result.unwrap_err();
3894    assert!(
3895      errors
3896        .iter()
3897        .any(|e| e.contains("--secure-heap must be a power of 2"))
3898    );
3899  }
3900
3901  #[test]
3902  fn test_secure_heap_min_not_power_of_two() {
3903    let result =
3904      parse_args(svec!["--secure-heap", "4", "--secure-heap-min", "3"]);
3905    assert!(result.is_err());
3906    let errors = result.unwrap_err();
3907    assert!(
3908      errors
3909        .iter()
3910        .any(|e| e.contains("--secure-heap-min must be a power of 2"))
3911    );
3912  }
3913
3914  #[test]
3915  fn test_deprecated_debug_options() {
3916    let result = parse_args(svec!["--debug"]);
3917    assert!(result.is_err());
3918    let errors = result.unwrap_err();
3919    assert!(errors.iter().any(|e| e.contains("[DEP0062]: `node --debug` and `node --debug-brk` are invalid. Please use `node --inspect` and `node --inspect-brk` instead.")));
3920  }
3921
3922  #[test]
3923  fn test_deprecated_debug_brk_options() {
3924    let result = parse_args(svec!["--debug-brk"]);
3925    assert!(result.is_err());
3926    let errors = result.unwrap_err();
3927    assert!(errors.iter().any(|e| e.contains("[DEP0062]: `node --debug` and `node --debug-brk` are invalid. Please use `node --inspect` and `node --inspect-brk` instead.")));
3928  }
3929
3930  #[test]
3931  fn test_invalid_inspect_publish_uid() {
3932    let result = parse_args(svec!["--inspect-publish-uid", "invalid"]);
3933    assert!(result.is_err());
3934    let errors = result.unwrap_err();
3935    assert!(errors.iter().any(|e| {
3936      e.contains("--inspect-publish-uid destination can be stderr or http")
3937    }));
3938  }
3939
3940  #[test]
3941  fn test_watch_requires_file_when_not_test() {
3942    let result = parse_args(svec!["--watch"]);
3943    assert!(result.is_err());
3944    let errors = result.unwrap_err();
3945    assert!(
3946      errors
3947        .iter()
3948        .any(|e| e.contains("--watch requires specifying a file"))
3949    );
3950  }
3951
3952  #[test]
3953  fn test_invalid_negation_for_non_boolean() {
3954    let result = parse_args(svec!["--no-title"]);
3955    assert!(result.is_err());
3956    let errors = result.unwrap_err();
3957    assert!(errors.iter().any(|e| {
3958      e.contains("is an invalid negation because it is not a boolean option")
3959    }));
3960  }
3961
3962  #[test]
3963  fn test_option_requires_argument() {
3964    let result = parse_args(svec!["--title"]);
3965    assert!(result.is_err());
3966    let errors = result.unwrap_err();
3967    assert!(
3968      errors
3969        .iter()
3970        .any(|e| e.contains("--title requires an argument"))
3971    );
3972  }
3973
3974  #[test]
3975  fn test_option_with_empty_equals_value() {
3976    let result = parse_args(svec!["--title="]);
3977    assert!(result.is_err());
3978    let errors = result.unwrap_err();
3979    assert!(
3980      errors
3981        .iter()
3982        .any(|e| e.contains("--title= requires an argument"))
3983    );
3984  }
3985
3986  #[test]
3987  fn test_option_with_dash_as_value() {
3988    let result = parse_args(svec!["--title", "-"]);
3989    assert!(result.is_err());
3990    let errors = result.unwrap_err();
3991    assert!(
3992      errors
3993        .iter()
3994        .any(|e| e.contains("--title requires an argument"))
3995    );
3996  }
3997
3998  #[test]
3999  fn test_invalid_port_range() {
4000    let result = parse_args(svec!["--inspect-port", "99999"]);
4001    assert!(result.is_err());
4002    let errors = result.unwrap_err();
4003    assert!(
4004      errors
4005        .iter()
4006        .any(|e| e.contains("must be 0 or in range 1024 to 65535"))
4007    );
4008  }
4009
4010  #[test]
4011  fn test_invalid_port_low() {
4012    let result = parse_args(svec!["--inspect-port", "500"]);
4013    assert!(result.is_err());
4014    let errors = result.unwrap_err();
4015    assert!(
4016      errors
4017        .iter()
4018        .any(|e| e.contains("must be 0 or in range 1024 to 65535"))
4019    );
4020  }
4021
4022  #[test]
4023  fn test_escaped_dash_in_value() {
4024    let result = parse_args(svec!["--title", "\\-mytitle"]).unwrap();
4025    assert_eq!(result.options.title, "-mytitle");
4026  }
4027
4028  #[test]
4029  fn test_compatible_profiling_options() {
4030    let result = parse_args(svec![
4031      "--cpu-prof",
4032      "--cpu-prof-name",
4033      "profile.log",
4034      "--cpu-prof-dir",
4035      "/tmp",
4036      "--cpu-prof-interval",
4037      "500"
4038    ])
4039    .unwrap();
4040    assert!(result.options.per_isolate.per_env.cpu_prof);
4041    assert_eq!(
4042      result.options.per_isolate.per_env.cpu_prof_name,
4043      "profile.log"
4044    );
4045    assert_eq!(result.options.per_isolate.per_env.cpu_prof_dir, "/tmp");
4046    assert_eq!(result.options.per_isolate.per_env.cpu_prof_interval, 500);
4047  }
4048
4049  #[test]
4050  fn test_compatible_heap_profiling_options() {
4051    let result = parse_args(svec![
4052      "--heap-prof",
4053      "--heap-prof-name",
4054      "heap.log",
4055      "--heap-prof-dir",
4056      "/tmp",
4057      "--heap-prof-interval",
4058      "1024"
4059    ])
4060    .unwrap();
4061    assert!(result.options.per_isolate.per_env.heap_prof);
4062    assert_eq!(
4063      result.options.per_isolate.per_env.heap_prof_name,
4064      "heap.log"
4065    );
4066    assert_eq!(result.options.per_isolate.per_env.heap_prof_dir, "/tmp");
4067    assert_eq!(result.options.per_isolate.per_env.heap_prof_interval, 1024);
4068  }
4069
4070  #[test]
4071  fn test_diagnostic_dir_used_for_prof_dirs() {
4072    let result = parse_args(svec![
4073      "--cpu-prof",
4074      "--heap-prof",
4075      "--diagnostic-dir",
4076      "/tmp/diag"
4077    ])
4078    .unwrap();
4079    assert_eq!(result.options.per_isolate.per_env.cpu_prof_dir, "/tmp/diag");
4080    assert_eq!(
4081      result.options.per_isolate.per_env.heap_prof_dir,
4082      "/tmp/diag"
4083    );
4084  }
4085
4086  #[test]
4087  fn test_valid_input_types() {
4088    for input_type in &[
4089      "commonjs",
4090      "module",
4091      "commonjs-typescript",
4092      "module-typescript",
4093    ] {
4094      let result = parse_args(svec!["--input-type", input_type]).unwrap();
4095      assert_eq!(result.options.per_isolate.per_env.input_type, *input_type);
4096    }
4097  }
4098
4099  #[test]
4100  fn test_valid_unhandled_rejections() {
4101    for rejection_type in
4102      &["warn-with-error-code", "throw", "strict", "warn", "none"]
4103    {
4104      let result =
4105        parse_args(svec!["--unhandled-rejections", rejection_type]).unwrap();
4106      assert_eq!(
4107        result.options.per_isolate.per_env.unhandled_rejections,
4108        *rejection_type
4109      );
4110    }
4111  }
4112
4113  #[test]
4114  fn test_valid_trace_require_module() {
4115    for trace_type in &["all", "no-node-modules"] {
4116      let result =
4117        parse_args(svec!["--trace-require-module", trace_type]).unwrap();
4118      assert_eq!(
4119        result.options.per_isolate.per_env.trace_require_module,
4120        *trace_type
4121      );
4122    }
4123  }
4124
4125  #[test]
4126  fn test_valid_test_isolation() {
4127    for isolation_type in &["process", "none"] {
4128      let result =
4129        parse_args(svec!["--test", "--test-isolation", isolation_type])
4130          .unwrap();
4131      assert_eq!(
4132        result.options.per_isolate.per_env.test_isolation,
4133        *isolation_type
4134      );
4135    }
4136  }
4137
4138  #[test]
4139  fn test_valid_use_largepages() {
4140    for largepages_type in &["off", "on", "silent"] {
4141      let result =
4142        parse_args(svec!["--use-largepages", largepages_type]).unwrap();
4143      assert_eq!(result.options.use_largepages, *largepages_type);
4144    }
4145  }
4146
4147  #[test]
4148  fn test_valid_inspect_publish_uid() {
4149    let result =
4150      parse_args(svec!["--inspect-publish-uid", "stderr,http"]).unwrap();
4151    assert!(
4152      result
4153        .options
4154        .per_isolate
4155        .per_env
4156        .debug_options
4157        .inspect_publish_uid
4158        .console
4159    );
4160    assert!(
4161      result
4162        .options
4163        .per_isolate
4164        .per_env
4165        .debug_options
4166        .inspect_publish_uid
4167        .http
4168    );
4169  }
4170
4171  #[test]
4172  fn test_valid_secure_heap_power_of_two() {
4173    let result =
4174      parse_args(svec!["--secure-heap", "4", "--secure-heap-min", "2"])
4175        .unwrap();
4176    assert_eq!(result.options.secure_heap, 4);
4177    assert_eq!(result.options.secure_heap_min, 2);
4178  }
4179
4180  #[test]
4181  fn test_implications_work() {
4182    // Test that --inspect-brk implies --inspect
4183    let result = parse_args(svec!["--inspect-brk"]).unwrap();
4184    assert!(
4185      result
4186        .options
4187        .per_isolate
4188        .per_env
4189        .debug_options
4190        .break_first_line
4191    );
4192    assert!(
4193      result
4194        .options
4195        .per_isolate
4196        .per_env
4197        .debug_options
4198        .inspector_enabled
4199    );
4200  }
4201
4202  #[test]
4203  fn test_alias_expansion_works() {
4204    // Test that -v expands to --version
4205    let result = parse_args(svec!["-v"]).unwrap();
4206    assert!(result.options.print_version);
4207
4208    // Test that -pe expands to --print --eval
4209    let result = parse_args(svec!["-pe", "console.log(42)"]).unwrap();
4210    assert!(result.options.per_isolate.per_env.print_eval);
4211    assert!(result.options.per_isolate.per_env.has_eval_string);
4212    assert_eq!(
4213      result.options.per_isolate.per_env.eval_string,
4214      "console.log(42)"
4215    );
4216  }
4217
4218  #[test]
4219  fn test_underscore_normalization() {
4220    // Test that underscores get normalized to dashes
4221    let result = parse_args(svec!["--zero_fill_buffers"]).unwrap();
4222    assert!(result.options.zero_fill_all_buffers);
4223  }
4224
4225  // ==================== Alias Expansion Tests ====================
4226
4227  #[test]
4228  fn test_prof_process_alias_does_not_infinite_loop() {
4229    // --prof-process should expand to ["--prof-process", "--"] but not recurse infinitely
4230    let result = parse_args(svec!["--prof-process", "somefile.log"]).unwrap();
4231    assert!(result.options.per_isolate.per_env.prof_process);
4232    // The remaining args should contain somefile.log, not multiple "--"
4233    assert_eq!(result.remaining_args, svec!["somefile.log"]);
4234  }
4235
4236  #[test]
4237  fn test_alias_short_c_to_check() {
4238    let result = parse_args(svec!["-c", "script.js"]).unwrap();
4239    assert!(result.options.per_isolate.per_env.syntax_check_only);
4240  }
4241
4242  #[test]
4243  fn test_alias_short_e_to_eval() {
4244    let result = parse_args(svec!["-e", "console.log(1)"]).unwrap();
4245    assert!(result.options.per_isolate.per_env.has_eval_string);
4246    assert_eq!(
4247      result.options.per_isolate.per_env.eval_string,
4248      "console.log(1)"
4249    );
4250  }
4251
4252  #[test]
4253  fn test_alias_short_p_to_print() {
4254    // -p expands to --print which is a boolean flag
4255    // The argument "42" becomes the script file, not the eval string
4256    // To get eval string behavior, use -pe (--print --eval)
4257    let result = parse_args(svec!["-p", "42"]).unwrap();
4258    assert!(result.options.per_isolate.per_env.print_eval);
4259    assert_eq!(result.remaining_args, svec!["42"]);
4260  }
4261
4262  #[test]
4263  fn test_alias_short_r_to_require() {
4264    let result = parse_args(svec!["-r", "dotenv/config", "script.js"]).unwrap();
4265    assert_eq!(
4266      result.options.per_isolate.per_env.preload_cjs_modules,
4267      svec!["dotenv/config"]
4268    );
4269  }
4270
4271  #[test]
4272  fn test_alias_short_i_to_interactive() {
4273    let result = parse_args(svec!["-i"]).unwrap();
4274    assert!(result.options.per_isolate.per_env.force_repl);
4275  }
4276
4277  #[test]
4278  fn test_alias_short_h_to_help() {
4279    let result = parse_args(svec!["-h"]).unwrap();
4280    assert!(result.options.print_help);
4281  }
4282
4283  #[test]
4284  fn test_alias_loader_to_experimental_loader() {
4285    let result =
4286      parse_args(svec!["--loader", "./my-loader.js", "script.js"]).unwrap();
4287    assert_eq!(
4288      result.options.per_isolate.per_env.userland_loaders,
4289      svec!["./my-loader.js"]
4290    );
4291  }
4292
4293  #[test]
4294  fn test_alias_conditions_short() {
4295    let result = parse_args(svec!["-C", "development", "script.js"]).unwrap();
4296    assert_eq!(
4297      result.options.per_isolate.per_env.conditions,
4298      svec!["development"]
4299    );
4300  }
4301
4302  // ==================== V8 Options Tests ====================
4303
4304  #[test]
4305  fn test_v8_option_max_old_space_size() {
4306    let result =
4307      parse_args(svec!["--max-old-space-size=4096", "script.js"]).unwrap();
4308    assert!(
4309      result
4310        .v8_args
4311        .contains(&"--max-old-space-size=4096".to_string())
4312    );
4313  }
4314
4315  #[test]
4316  fn test_v8_option_max_semi_space_size() {
4317    let result =
4318      parse_args(svec!["--max-semi-space-size=64", "script.js"]).unwrap();
4319    assert!(
4320      result
4321        .v8_args
4322        .contains(&"--max-semi-space-size=64".to_string())
4323    );
4324  }
4325
4326  #[test]
4327  fn test_v8_option_expose_gc() {
4328    let result = parse_args(svec!["--expose-gc", "script.js"]).unwrap();
4329    assert!(result.v8_args.contains(&"--expose-gc".to_string()));
4330  }
4331
4332  #[test]
4333  fn test_multiple_v8_options() {
4334    let result = parse_args(svec![
4335      "--max-old-space-size=4096",
4336      "--expose-gc",
4337      "script.js"
4338    ])
4339    .unwrap();
4340    assert!(
4341      result
4342        .v8_args
4343        .contains(&"--max-old-space-size=4096".to_string())
4344    );
4345    assert!(result.v8_args.contains(&"--expose-gc".to_string()));
4346  }
4347
4348  // ==================== Remaining Args / Script Parsing Tests ====================
4349
4350  #[test]
4351  fn test_script_with_args() {
4352    let result =
4353      parse_args(svec!["script.js", "arg1", "arg2", "--flag"]).unwrap();
4354    assert_eq!(
4355      result.remaining_args,
4356      svec!["script.js", "arg1", "arg2", "--flag"]
4357    );
4358  }
4359
4360  #[test]
4361  fn test_options_before_script() {
4362    let result =
4363      parse_args(svec!["--no-warnings", "script.js", "--my-arg"]).unwrap();
4364    assert!(!result.options.per_isolate.per_env.warnings);
4365    assert_eq!(result.remaining_args, svec!["script.js", "--my-arg"]);
4366  }
4367
4368  #[test]
4369  fn test_double_dash_stops_option_parsing() {
4370    let result = parse_args(svec!["--", "--version"]).unwrap();
4371    // --version after -- should be treated as a script name, not an option
4372    assert!(!result.options.print_version);
4373    assert_eq!(result.remaining_args, svec!["--version"]);
4374  }
4375
4376  #[test]
4377  fn test_double_dash_with_script_and_args() {
4378    let result =
4379      parse_args(svec!["--no-warnings", "--", "script.js", "--help"]).unwrap();
4380    assert!(!result.options.per_isolate.per_env.warnings);
4381    assert_eq!(result.remaining_args, svec!["script.js", "--help"]);
4382  }
4383
4384  // ==================== --run, --eval, --test Options Tests ====================
4385
4386  #[test]
4387  fn test_run_option() {
4388    let result = parse_args(svec!["--run", "build"]).unwrap();
4389    assert_eq!(result.options.run, "build");
4390  }
4391
4392  #[test]
4393  fn test_eval_option_with_code() {
4394    let result = parse_args(svec!["--eval", "console.log('hello')"]).unwrap();
4395    assert!(result.options.per_isolate.per_env.has_eval_string);
4396    assert_eq!(
4397      result.options.per_isolate.per_env.eval_string,
4398      "console.log('hello')"
4399    );
4400  }
4401
4402  #[test]
4403  fn test_print_eval_option() {
4404    // --print is a boolean flag; use -pe for print+eval
4405    let result = parse_args(svec!["-pe", "1 + 1"]).unwrap();
4406    assert!(result.options.per_isolate.per_env.print_eval);
4407    assert!(result.options.per_isolate.per_env.has_eval_string);
4408    assert_eq!(result.options.per_isolate.per_env.eval_string, "1 + 1");
4409  }
4410
4411  #[test]
4412  fn test_test_runner_option() {
4413    let result = parse_args(svec!["--test"]).unwrap();
4414    assert!(result.options.per_isolate.per_env.test_runner);
4415  }
4416
4417  #[test]
4418  fn test_test_with_files() {
4419    let result = parse_args(svec!["--test", "test/*.js"]).unwrap();
4420    assert!(result.options.per_isolate.per_env.test_runner);
4421    assert_eq!(result.remaining_args, svec!["test/*.js"]);
4422  }
4423
4424  #[test]
4425  fn test_test_timeout_option() {
4426    let result = parse_args(svec!["--test", "--test-timeout", "5000"]).unwrap();
4427    assert!(result.options.per_isolate.per_env.test_runner);
4428    assert_eq!(result.options.per_isolate.per_env.test_runner_timeout, 5000);
4429  }
4430
4431  #[test]
4432  fn test_test_concurrency_option() {
4433    let result =
4434      parse_args(svec!["--test", "--test-concurrency", "4"]).unwrap();
4435    assert!(result.options.per_isolate.per_env.test_runner);
4436    assert_eq!(
4437      result.options.per_isolate.per_env.test_runner_concurrency,
4438      4
4439    );
4440  }
4441
4442  #[test]
4443  fn test_test_name_pattern_option() {
4444    let result =
4445      parse_args(svec!["--test", "--test-name-pattern", "should.*work"])
4446        .unwrap();
4447    assert!(result.options.per_isolate.per_env.test_runner);
4448    assert_eq!(
4449      result.options.per_isolate.per_env.test_name_pattern,
4450      svec!["should.*work"]
4451    );
4452  }
4453
4454  #[test]
4455  fn test_test_skip_pattern_option() {
4456    let result =
4457      parse_args(svec!["--test", "--test-skip-pattern", "slow"]).unwrap();
4458    assert!(result.options.per_isolate.per_env.test_runner);
4459    assert_eq!(
4460      result.options.per_isolate.per_env.test_skip_pattern,
4461      svec!["slow"]
4462    );
4463  }
4464
4465  // ==================== String List Options Tests ====================
4466
4467  #[test]
4468  fn test_multiple_conditions() {
4469    let result = parse_args(svec![
4470      "--conditions",
4471      "development",
4472      "--conditions",
4473      "browser",
4474      "script.js"
4475    ])
4476    .unwrap();
4477    assert_eq!(
4478      result.options.per_isolate.per_env.conditions,
4479      svec!["development", "browser"]
4480    );
4481  }
4482
4483  #[test]
4484  fn test_multiple_require() {
4485    let result = parse_args(svec![
4486      "--require",
4487      "dotenv/config",
4488      "--require",
4489      "./setup.js",
4490      "script.js"
4491    ])
4492    .unwrap();
4493    assert_eq!(
4494      result.options.per_isolate.per_env.preload_cjs_modules,
4495      svec!["dotenv/config", "./setup.js"]
4496    );
4497  }
4498
4499  #[test]
4500  fn test_import_option() {
4501    let result =
4502      parse_args(svec!["--import", "./register.js", "script.js"]).unwrap();
4503    assert_eq!(
4504      result.options.per_isolate.per_env.preload_esm_modules,
4505      svec!["./register.js"]
4506    );
4507  }
4508
4509  #[test]
4510  fn test_multiple_import() {
4511    let result = parse_args(svec![
4512      "--import",
4513      "./a.js",
4514      "--import",
4515      "./b.js",
4516      "script.js"
4517    ])
4518    .unwrap();
4519    assert_eq!(
4520      result.options.per_isolate.per_env.preload_esm_modules,
4521      svec!["./a.js", "./b.js"]
4522    );
4523  }
4524
4525  // ==================== Watch Mode Tests ====================
4526
4527  #[test]
4528  fn test_watch_with_script() {
4529    let result = parse_args(svec!["--watch", "script.js"]).unwrap();
4530    assert!(result.options.per_isolate.per_env.watch_mode);
4531    assert_eq!(result.remaining_args, svec!["script.js"]);
4532  }
4533
4534  #[test]
4535  fn test_watch_with_test() {
4536    let result = parse_args(svec!["--test", "--watch"]).unwrap();
4537    assert!(result.options.per_isolate.per_env.test_runner);
4538    assert!(result.options.per_isolate.per_env.watch_mode);
4539  }
4540
4541  #[test]
4542  fn test_watch_path_option() {
4543    let result =
4544      parse_args(svec!["--watch", "--watch-path", "./src", "script.js"])
4545        .unwrap();
4546    assert!(result.options.per_isolate.per_env.watch_mode);
4547    assert_eq!(
4548      result.options.per_isolate.per_env.watch_mode_paths,
4549      svec!["./src"]
4550    );
4551  }
4552
4553  #[test]
4554  fn test_watch_preserve_output() {
4555    let result =
4556      parse_args(svec!["--watch", "--watch-preserve-output", "script.js"])
4557        .unwrap();
4558    assert!(result.options.per_isolate.per_env.watch_mode);
4559    assert!(
4560      result
4561        .options
4562        .per_isolate
4563        .per_env
4564        .watch_mode_preserve_output
4565    );
4566  }
4567
4568  // ==================== NODE_OPTIONS Edge Cases Tests ====================
4569
4570  #[test]
4571  fn test_node_options_with_escaped_quotes() {
4572    let env_args =
4573      parse_node_options_env_var("--title \"hello \\\"world\\\"\"").unwrap();
4574    assert_eq!(env_args, vec!["--title", "hello \"world\""]);
4575  }
4576
4577  #[test]
4578  fn test_node_options_with_backslash() {
4579    let env_args =
4580      parse_node_options_env_var("--title \"path\\\\to\\\\file\"").unwrap();
4581    assert_eq!(env_args, vec!["--title", "path\\to\\file"]);
4582  }
4583
4584  #[test]
4585  fn test_node_options_multiple_spaces() {
4586    let env_args =
4587      parse_node_options_env_var("--no-warnings   --no-deprecation").unwrap();
4588    assert_eq!(env_args, vec!["--no-warnings", "--no-deprecation"]);
4589  }
4590
4591  #[test]
4592  fn test_node_options_unterminated_string_error() {
4593    let result = parse_node_options_env_var("--title \"unterminated");
4594    assert!(result.is_err());
4595    let errors = result.unwrap_err();
4596    assert!(errors.iter().any(|e| e.contains("unterminated string")));
4597  }
4598
4599  #[test]
4600  fn test_node_options_invalid_escape_error() {
4601    let result = parse_node_options_env_var("--title \"test\\");
4602    assert!(result.is_err());
4603    let errors = result.unwrap_err();
4604    assert!(errors.iter().any(|e| e.contains("invalid escape")));
4605  }
4606
4607  #[test]
4608  fn test_node_options_empty_string() {
4609    let env_args = parse_node_options_env_var("").unwrap();
4610    assert!(env_args.is_empty());
4611  }
4612
4613  #[test]
4614  fn test_node_options_only_spaces() {
4615    let env_args = parse_node_options_env_var("   ").unwrap();
4616    assert!(env_args.is_empty());
4617  }
4618
4619  // ==================== Debug / Inspector Options Tests ====================
4620
4621  #[test]
4622  fn test_inspect_brk_option() {
4623    let result = parse_args(svec!["--inspect-brk"]).unwrap();
4624    assert!(
4625      result
4626        .options
4627        .per_isolate
4628        .per_env
4629        .debug_options
4630        .inspector_enabled
4631    );
4632    assert!(
4633      result
4634        .options
4635        .per_isolate
4636        .per_env
4637        .debug_options
4638        .break_first_line
4639    );
4640  }
4641
4642  #[test]
4643  fn test_inspect_wait_option() {
4644    let result = parse_args(svec!["--inspect-wait"]).unwrap();
4645    assert!(
4646      result
4647        .options
4648        .per_isolate
4649        .per_env
4650        .debug_options
4651        .inspector_enabled
4652    );
4653    assert!(
4654      result
4655        .options
4656        .per_isolate
4657        .per_env
4658        .debug_options
4659        .inspect_wait
4660    );
4661  }
4662
4663  #[test]
4664  fn test_inspect_with_custom_port() {
4665    let result = parse_args(svec!["--inspect=9230"]).unwrap();
4666    assert!(
4667      result
4668        .options
4669        .per_isolate
4670        .per_env
4671        .debug_options
4672        .inspector_enabled
4673    );
4674    assert_eq!(
4675      result
4676        .options
4677        .per_isolate
4678        .per_env
4679        .debug_options
4680        .host_port
4681        .port,
4682      9230
4683    );
4684  }
4685
4686  #[test]
4687  fn test_inspect_with_host_and_port() {
4688    let result = parse_args(svec!["--inspect=0.0.0.0:9230"]).unwrap();
4689    assert!(
4690      result
4691        .options
4692        .per_isolate
4693        .per_env
4694        .debug_options
4695        .inspector_enabled
4696    );
4697    assert_eq!(
4698      result
4699        .options
4700        .per_isolate
4701        .per_env
4702        .debug_options
4703        .host_port
4704        .host,
4705      "0.0.0.0"
4706    );
4707    assert_eq!(
4708      result
4709        .options
4710        .per_isolate
4711        .per_env
4712        .debug_options
4713        .host_port
4714        .port,
4715      9230
4716    );
4717  }
4718
4719  #[test]
4720  fn test_inspect_port_zero() {
4721    let result = parse_args(svec!["--inspect-port", "0"]).unwrap();
4722    assert_eq!(
4723      result
4724        .options
4725        .per_isolate
4726        .per_env
4727        .debug_options
4728        .host_port
4729        .port,
4730      0
4731    );
4732  }
4733
4734  // ==================== Env File Options Tests ====================
4735
4736  #[test]
4737  fn test_env_file_option() {
4738    let result = parse_args(svec!["--env-file", ".env", "script.js"]).unwrap();
4739    assert!(result.options.per_isolate.per_env.has_env_file_string);
4740    assert_eq!(result.options.per_isolate.per_env.env_file, ".env");
4741  }
4742
4743  #[test]
4744  fn test_env_file_if_exists_option() {
4745    let result =
4746      parse_args(svec!["--env-file-if-exists", ".env.local", "script.js"])
4747        .unwrap();
4748    assert!(result.options.per_isolate.per_env.has_env_file_string);
4749    assert_eq!(
4750      result.options.per_isolate.per_env.optional_env_file,
4751      ".env.local"
4752    );
4753  }
4754
4755  // ==================== Boolean Options Tests ====================
4756
4757  #[test]
4758  fn test_no_deprecation() {
4759    let result = parse_args(svec!["--no-deprecation"]).unwrap();
4760    assert!(!result.options.per_isolate.per_env.deprecation);
4761  }
4762
4763  #[test]
4764  fn test_throw_deprecation() {
4765    let result = parse_args(svec!["--throw-deprecation"]).unwrap();
4766    assert!(result.options.per_isolate.per_env.throw_deprecation);
4767  }
4768
4769  #[test]
4770  fn test_trace_deprecation() {
4771    let result = parse_args(svec!["--trace-deprecation"]).unwrap();
4772    assert!(result.options.per_isolate.per_env.trace_deprecation);
4773  }
4774
4775  #[test]
4776  fn test_pending_deprecation() {
4777    let result = parse_args(svec!["--pending-deprecation"]).unwrap();
4778    assert!(result.options.per_isolate.per_env.pending_deprecation);
4779  }
4780
4781  #[test]
4782  fn test_preserve_symlinks() {
4783    let result = parse_args(svec!["--preserve-symlinks"]).unwrap();
4784    assert!(result.options.per_isolate.per_env.preserve_symlinks);
4785  }
4786
4787  #[test]
4788  fn test_preserve_symlinks_main() {
4789    let result = parse_args(svec!["--preserve-symlinks-main"]).unwrap();
4790    assert!(result.options.per_isolate.per_env.preserve_symlinks_main);
4791  }
4792
4793  #[test]
4794  fn test_no_extra_info_on_fatal_exception() {
4795    let result =
4796      parse_args(svec!["--no-extra-info-on-fatal-exception"]).unwrap();
4797    assert!(
4798      !result
4799        .options
4800        .per_isolate
4801        .per_env
4802        .extra_info_on_fatal_exception
4803    );
4804  }
4805
4806  #[test]
4807  fn test_enable_source_maps() {
4808    let result = parse_args(svec!["--enable-source-maps"]).unwrap();
4809    assert!(result.options.per_isolate.per_env.enable_source_maps);
4810  }
4811
4812  #[test]
4813  fn test_experimental_strip_types() {
4814    let result = parse_args(svec!["--experimental-strip-types"]).unwrap();
4815    assert!(result.options.per_isolate.per_env.experimental_strip_types);
4816  }
4817
4818  // ==================== Equals Syntax Tests ====================
4819
4820  #[test]
4821  fn test_option_with_equals() {
4822    let result = parse_args(svec!["--title=myapp"]).unwrap();
4823    assert_eq!(result.options.title, "myapp");
4824  }
4825
4826  #[test]
4827  fn test_option_with_equals_and_spaces_in_value() {
4828    // Value with spaces requires quoting at shell level, but once parsed it works
4829    let result = parse_args(svec!["--title=my app"]).unwrap();
4830    assert_eq!(result.options.title, "my app");
4831  }
4832
4833  #[test]
4834  fn test_boolean_option_positive() {
4835    // Boolean options are set to true by using them directly
4836    let result = parse_args(svec!["--warnings"]).unwrap();
4837    assert!(result.options.per_isolate.per_env.warnings);
4838  }
4839
4840  #[test]
4841  fn test_boolean_option_negative() {
4842    // Boolean options are set to false using --no- prefix
4843    let result = parse_args(svec!["--no-warnings"]).unwrap();
4844    assert!(!result.options.per_isolate.per_env.warnings);
4845  }
4846
4847  #[test]
4848  fn test_boolean_option_double_negation() {
4849    // --no-deprecation disables deprecation warnings
4850    let result = parse_args(svec!["--no-deprecation"]).unwrap();
4851    assert!(!result.options.per_isolate.per_env.deprecation);
4852  }
4853
4854  // ==================== Unknown Option Tests ====================
4855
4856  #[test]
4857  fn test_unknown_option_passed_to_v8() {
4858    // Unknown options are passed through as V8 args, not treated as errors
4859    let result =
4860      parse_args(svec!["--unknown-option-that-does-not-exist", "script.js"])
4861        .unwrap();
4862    assert!(
4863      result
4864        .v8_args
4865        .contains(&"--unknown-option-that-does-not-exist".to_string())
4866    );
4867  }
4868
4869  // ==================== Integer Option Tests ====================
4870
4871  #[test]
4872  fn test_stack_trace_limit() {
4873    let result = parse_args(svec!["--stack-trace-limit", "50"]).unwrap();
4874    assert_eq!(result.options.per_isolate.stack_trace_limit, 50);
4875  }
4876
4877  #[test]
4878  fn test_v8_pool_size() {
4879    let result = parse_args(svec!["--v8-pool-size", "8"]).unwrap();
4880    assert_eq!(result.options.v8_thread_pool_size, 8);
4881  }
4882
4883  // ==================== Complex Scenarios Tests ====================
4884
4885  #[test]
4886  fn test_combined_options_for_debugging() {
4887    let result = parse_args(svec![
4888      "--inspect-brk=9230",
4889      "--no-warnings",
4890      "--enable-source-maps",
4891      "script.js",
4892      "--arg1"
4893    ])
4894    .unwrap();
4895    assert!(
4896      result
4897        .options
4898        .per_isolate
4899        .per_env
4900        .debug_options
4901        .inspector_enabled
4902    );
4903    assert!(
4904      result
4905        .options
4906        .per_isolate
4907        .per_env
4908        .debug_options
4909        .break_first_line
4910    );
4911    assert_eq!(
4912      result
4913        .options
4914        .per_isolate
4915        .per_env
4916        .debug_options
4917        .host_port
4918        .port,
4919      9230
4920    );
4921    assert!(!result.options.per_isolate.per_env.warnings);
4922    assert!(result.options.per_isolate.per_env.enable_source_maps);
4923    assert_eq!(result.remaining_args, svec!["script.js", "--arg1"]);
4924  }
4925
4926  #[test]
4927  fn test_combined_options_for_testing() {
4928    let result = parse_args(svec![
4929      "--test",
4930      "--test-timeout",
4931      "10000",
4932      "--test-concurrency",
4933      "2",
4934      "--test-reporter",
4935      "spec",
4936      "test/**/*.test.js"
4937    ])
4938    .unwrap();
4939    assert!(result.options.per_isolate.per_env.test_runner);
4940    assert_eq!(
4941      result.options.per_isolate.per_env.test_runner_timeout,
4942      10000
4943    );
4944    assert_eq!(
4945      result.options.per_isolate.per_env.test_runner_concurrency,
4946      2
4947    );
4948    assert_eq!(
4949      result.options.per_isolate.per_env.test_reporter,
4950      svec!["spec"]
4951    );
4952    assert_eq!(result.remaining_args, svec!["test/**/*.test.js"]);
4953  }
4954
4955  #[test]
4956  fn test_combined_options_for_esm_loader() {
4957    let result = parse_args(svec![
4958      "--import",
4959      "./register.js",
4960      "--conditions",
4961      "development",
4962      "script.js"
4963    ])
4964    .unwrap();
4965    assert_eq!(
4966      result.options.per_isolate.per_env.preload_esm_modules,
4967      svec!["./register.js"]
4968    );
4969    assert_eq!(
4970      result.options.per_isolate.per_env.conditions,
4971      svec!["development"]
4972    );
4973    assert_eq!(result.remaining_args, svec!["script.js"]);
4974  }
4975
4976  /// Test that takes a `input: ["node"]` and `expected: ["deno", "repl", "-A", "--"] `
4977  macro_rules! test {
4978      ($name:ident, $input:tt , $expected:tt) => {
4979          #[test]
4980          fn $name() {
4981              let parsed_args = parse_args(svec! $input).unwrap();
4982              let options = TranslateOptions::for_node_cli();
4983              let result = translate_to_deno_args(parsed_args, &options);
4984              assert_eq!(result.deno_args, svec! $expected);
4985          }
4986      };
4987  }
4988
4989  test!(test_repl_no_args, [], ["node", "repl", "-A", "--"]);
4990
4991  test!(
4992    test_run_script,
4993    ["foo.js"],
4994    [
4995      "node",
4996      "run",
4997      "-A",
4998      "--unstable-node-globals",
4999      "--unstable-bare-node-builtins",
5000      "--unstable-detect-cjs",
5001      "--node-modules-dir=manual",
5002      "--no-config",
5003      "foo.js"
5004    ]
5005  );
5006}