1use std::{collections::VecDeque, iter::from_fn, ops::Range};
2
3use indexmap::IndexSet;
4use petgraph::EdgeDirection::{Incoming as Dependants, Outgoing as Dependencies};
5use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
6use swc_common::{sync::Lrc, util::take::Take, SourceMap, SyntaxContext};
7use swc_ecma_ast::*;
8use swc_ecma_utils::find_pat_ids;
9use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
10
11use super::graph::Required;
12use crate::{id::Id, modules::sort::graph::StmtDepGraph, util::is_injected};
13
14pub(super) fn sort_stmts(
15 injected_ctxt: SyntaxContext,
16 modules: Vec<Vec<ModuleItem>>,
17 _cm: &Lrc<SourceMap>,
18) -> Vec<ModuleItem> {
19 let total_len: usize = modules.iter().map(|v| v.len()).sum();
20
21 let mut stmts = Vec::new();
22 let mut free = Vec::new();
23 let mut same_module_ranges = Vec::new();
24 let mut module_starts = Vec::new();
25
26 for module in modules {
27 let start = stmts.len();
28 module_starts.push(stmts.len());
29 let mut module_item_count = 0;
30
31 for stmt in module {
32 if is_injected_module_item(&stmt, injected_ctxt) {
33 free.push(stmt)
34 } else {
35 module_item_count += 1;
36 stmts.push(stmt)
37 }
38 }
39
40 same_module_ranges.push(start..start + module_item_count);
41 }
42 let non_free_count = stmts.len();
43 let free_range = non_free_count..non_free_count + free.len();
44 stmts.extend(free);
45
46 let mut id_graph = calc_deps(&stmts);
57
58 tracing::debug!("Analyzed dependencies between statements");
59
60 let orders = iter(
61 &mut id_graph,
62 &same_module_ranges,
63 free_range,
64 &module_starts,
65 &stmts,
66 )
67 .collect::<Vec<_>>();
68
69 tracing::debug!("Sorted statements");
70
71 debug_assert_eq!(total_len, orders.len());
72
73 let mut new = Vec::with_capacity(stmts.len());
74 for idx in orders {
75 new.push(stmts[idx].take());
76 }
77
78 new
79}
80
81fn is_injected_module_item(stmt: &ModuleItem, injected_ctxt: SyntaxContext) -> bool {
82 match stmt {
83 ModuleItem::ModuleDecl(
84 ModuleDecl::Import(ImportDecl {
85 with: Some(with), ..
86 })
87 | ModuleDecl::ExportAll(ExportAll {
88 with: Some(with), ..
89 })
90 | ModuleDecl::ExportNamed(NamedExport {
91 with: Some(with), ..
92 }),
93 ) => is_injected(with),
94
95 ModuleItem::Stmt(Stmt::Decl(Decl::Var(v))) => v.ctxt == injected_ctxt,
96
97 _ => false,
98 }
99}
100
101fn iter<'a>(
102 graph: &'a mut StmtDepGraph,
103 same_module_ranges: &'a [Range<usize>],
104 free: Range<usize>,
105 module_starts: &[usize],
106 stmts: &'a [ModuleItem],
107) -> impl 'a + Iterator<Item = usize> {
108 let len = graph.node_count();
109
110 let mut moves = FxHashSet::default();
115 let mut done = FxHashSet::default();
116 let mut stack = VecDeque::new();
117 stack.extend(module_starts.iter().copied());
118
119 for &start in module_starts {
120 let range = same_module_ranges
121 .iter()
122 .find(|range| range.contains(&start))
123 .cloned();
124 if let Some(range) = range {
125 for v in range {
126 stack.push_back(v);
127 }
128 }
129 }
130 for v in free.clone() {
131 stack.push_back(v);
132 }
133
134 from_fn(move || {
135 if done.len() == len {
136 return None;
137 }
138
139 'main: while let Some(idx) = stack.pop_front() {
141 if done.contains(&idx) {
142 continue;
144 }
145 let is_free = free.contains(&idx);
146
147 let current_range = same_module_ranges
163 .iter()
164 .find(|range| range.contains(&idx))
165 .cloned();
166
167 let can_ignore_deps = match &stmts[idx] {
170 ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
171 decl: Decl::Fn(..),
172 ..
173 }))
174 | ModuleItem::Stmt(Stmt::Decl(Decl::Fn(..))) => true,
175
176 ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
177 decl: Decl::Class(cls),
178 ..
179 }))
180 | ModuleItem::Stmt(Stmt::Decl(Decl::Class(cls)))
181 if cls.class.super_class.is_none() =>
182 {
183 true
184 }
185
186 _ => false,
187 };
188 let can_ignore_weak_deps = can_ignore_deps
189 || matches!(
190 &stmts[idx],
191 ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
192 decl: Decl::Class(..),
193 ..
194 })) | ModuleItem::Stmt(Stmt::Decl(Decl::Class(..)))
195 );
196
197 {
199 let deps = graph
200 .neighbors_directed(idx, Dependencies)
201 .filter(|dep| {
202 let declared_in_same_module = match ¤t_range {
203 Some(v) => v.contains(dep),
204 None => false,
205 };
206 if declared_in_same_module {
207 return false;
208 }
209
210 if !free.contains(&idx)
211 && graph.has_a_path(*dep, idx)
212 && !moves.insert((idx, *dep))
213 {
214 return false;
215 }
216
217 !done.contains(dep)
219 })
220 .collect::<Vec<_>>();
221
222 if !deps.is_empty() {
225 let mut deps_to_push = Vec::new();
226 for dep in deps.iter().rev().copied() {
227 if deps_to_push.contains(&dep) {
228 continue;
229 }
230
231 let can_ignore_dep = can_ignore_deps
232 || (can_ignore_weak_deps
233 && graph.edge_weight(idx, dep) == Some(Required::Maybe));
234
235 if can_ignore_dep && graph.has_a_path(dep, idx) {
236 continue;
238 }
239
240 deps_to_push.push(dep);
241 }
242
243 if !deps_to_push.is_empty() {
246 stack.push_front(idx);
248
249 for dep in deps_to_push {
250 stack.push_front(dep)
252 }
253
254 continue;
255 }
256 }
257 }
258
259 if is_free {
260 let dependants = graph
261 .neighbors_directed(idx, Dependants)
262 .collect::<Vec<_>>();
263
264 for dependant in dependants {
265 if !done.contains(&dependant) && free.contains(&dependant) {
266 stack.push_front(dependant);
267 }
268 }
269
270 graph.remove_node(idx);
271 done.insert(idx);
272 return Some(idx);
273 }
274
275 let current_range = match current_range {
276 Some(v) => v,
277 None => {
278 let dependants = graph
279 .neighbors_directed(idx, Dependants)
280 .collect::<Vec<_>>();
281
282 for dependant in dependants {
287 if !done.contains(&dependant) && free.contains(&dependant) {
288 stack.push_front(dependant);
289 }
290 }
291
292 graph.remove_node(idx);
294 done.insert(idx);
295 return Some(idx);
296 }
297 };
298
299 for preceding in current_range.clone() {
301 if done.contains(&preceding) {
303 continue;
304 }
305 if preceding == idx {
307 continue;
308 }
309 if preceding > idx {
310 break;
311 }
312
313 if !moves.insert((idx, preceding)) {
314 continue;
316 }
317
318 let dependants = graph
319 .neighbors_directed(idx, Dependants)
320 .collect::<Vec<_>>();
321
322 for dependant in dependants {
327 if !done.contains(&dependant) && free.contains(&dependant) {
328 stack.push_front(dependant);
329 }
330 }
331
332 stack.push_front(idx);
334 stack.push_front(preceding);
335 continue 'main;
336 }
337
338 graph.remove_node(idx);
339 done.insert(idx);
340 return Some(idx);
341 }
342
343 None
344 })
345}
346
347#[derive(Default)]
349struct FieldInitFinder {
350 in_object_assign: bool,
351 in_rhs: bool,
352 accessed: FxHashSet<Id>,
353}
354
355impl FieldInitFinder {
356 fn check_lhs_of_assign(&mut self, lhs: &AssignTarget) {
357 if let AssignTarget::Simple(SimpleAssignTarget::Member(m)) = lhs {
358 match &*m.obj {
359 Expr::Ident(i) => {
360 self.accessed.insert(i.into());
361 }
362 Expr::Member(..) => self.check_lhs_expr_of_assign(&m.obj),
363 _ => {}
364 }
365 }
366 }
367
368 fn check_lhs_expr_of_assign(&mut self, lhs: &Expr) {
369 if let Expr::Member(m) = lhs {
370 match &*m.obj {
371 Expr::Ident(i) => {
372 self.accessed.insert(i.into());
373 }
374 Expr::Member(..) => self.check_lhs_expr_of_assign(&m.obj),
375 _ => {}
376 }
377 }
378 }
379}
380
381impl Visit for FieldInitFinder {
382 noop_visit_type!();
383
384 fn visit_assign_expr(&mut self, e: &AssignExpr) {
385 let old = self.in_rhs;
386 self.in_rhs = false;
387 e.left.visit_with(self);
388 self.check_lhs_of_assign(&e.left);
389
390 self.in_rhs = true;
391 e.right.visit_with(self);
392 self.in_rhs = old;
393 }
394
395 fn visit_pat(&mut self, e: &Pat) {
396 let old = self.in_rhs;
397 self.in_rhs = false;
398 e.visit_children_with(self);
399 self.in_rhs = old;
400 }
401
402 fn visit_call_expr(&mut self, e: &CallExpr) {
403 match &e.callee {
404 Callee::Super(_) | Callee::Import(_) => {}
405 Callee::Expr(callee) => {
406 if let Expr::Member(callee) = &**callee {
407 if callee.obj.is_ident_ref_to("Object") {
408 match &callee.prop {
409 MemberProp::Ident(IdentName { sym: prop_sym, .. })
410 if *prop_sym == *"assign" =>
411 {
412 let old = self.in_object_assign;
413 self.in_object_assign = true;
414
415 e.args.visit_with(self);
416 self.in_object_assign = old;
417
418 return;
419 }
420 _ => {}
421 }
422 }
423 }
424 }
425 #[cfg(swc_ast_unknown)]
426 _ => panic!("unable to access unknown nodes"),
427 }
428
429 e.visit_children_with(self);
430 }
431
432 fn visit_member_expr(&mut self, e: &MemberExpr) {
433 e.obj.visit_with(self);
434
435 if let MemberProp::Computed(c) = &e.prop {
436 c.visit_with(self);
437 }
438
439 if !self.in_rhs || self.in_object_assign {
440 if let Expr::Ident(obj) = &*e.obj {
441 match &e.prop {
442 MemberProp::Ident(IdentName { sym: prop_sym, .. })
443 if *prop_sym == *"prototype" =>
444 {
445 self.accessed.insert(obj.into());
446 }
447 _ => {}
448 }
449 }
450 }
451 }
452
453 fn visit_super_prop_expr(&mut self, e: &SuperPropExpr) {
454 if let SuperProp::Computed(c) = &e.prop {
455 c.visit_with(self);
456 }
457 }
458}
459
460struct InitializerFinder {
462 ident: Id,
463 found: bool,
464 in_complex: bool,
465}
466
467impl Visit for InitializerFinder {
468 noop_visit_type!();
469
470 fn visit_pat(&mut self, pat: &Pat) {
471 match pat {
472 Pat::Ident(i) if self.ident == i.id => {
473 self.found = true;
474 }
475
476 _ => {
477 pat.visit_children_with(self);
478 }
479 }
480 }
481
482 fn visit_ident(&mut self, i: &Ident) {
483 if self.in_complex && self.ident == *i {
484 self.found = true;
485 }
486 }
487
488 fn visit_expr_or_spread(&mut self, node: &ExprOrSpread) {
489 let in_complex = self.in_complex;
490 self.in_complex = true;
491 node.visit_children_with(self);
492 self.in_complex = in_complex;
493 }
494
495 fn visit_member_expr(&mut self, e: &MemberExpr) {
496 {
497 let in_complex = self.in_complex;
498 self.in_complex = true;
499 e.obj.visit_children_with(self);
500 self.in_complex = in_complex;
501 }
502
503 if let MemberProp::Computed(c) = &e.prop {
504 c.visit_with(self);
505 }
506 }
507
508 fn visit_super_prop_expr(&mut self, e: &SuperPropExpr) {
509 if let SuperProp::Computed(c) = &e.prop {
510 c.visit_with(self);
511 }
512 }
513}
514
515#[derive(Default)]
518struct RequirementCalculator {
519 required_ids: IndexSet<(Id, Required), FxBuildHasher>,
520 excluded: IndexSet<Id, FxBuildHasher>,
524
525 in_weak: bool,
526 in_var_decl: bool,
527 in_assign_lhs: bool,
528}
529
530macro_rules! weak {
531 ($name:ident, $T:ty) => {
532 fn $name(&mut self, f: &$T) {
533 let in_weak = self.in_weak;
534 self.in_weak = true;
535
536 f.visit_children_with(self);
537
538 self.in_weak = in_weak;
539 }
540 };
541}
542
543impl RequirementCalculator {
544 fn insert(&mut self, i: Id) {
545 self.required_ids.insert((
546 i,
547 if self.in_weak {
548 Required::Maybe
549 } else {
550 Required::Always
551 },
552 ));
553 }
554}
555
556impl Visit for RequirementCalculator {
557 noop_visit_type!();
558
559 weak!(visit_arrow_expr, ArrowExpr);
560
561 weak!(visit_function, Function);
562
563 weak!(visit_class_method, ClassMethod);
564
565 weak!(visit_private_method, PrivateMethod);
566
567 weak!(visit_method_prop, MethodProp);
568
569 fn visit_export_named_specifier(&mut self, n: &ExportNamedSpecifier) {
570 let orig = match &n.orig {
571 ModuleExportName::Ident(ident) => ident,
572 ModuleExportName::Str(..) => unimplemented!("module string names unimplemented"),
573 #[cfg(swc_ast_unknown)]
574 _ => panic!("unable to access unknown nodes"),
575 };
576 self.insert(orig.clone().into());
577 }
578
579 fn visit_assign_expr(&mut self, e: &AssignExpr) {
580 let old = self.in_assign_lhs;
581
582 self.in_assign_lhs = true;
583 e.left.visit_with(self);
584
585 self.in_assign_lhs = false;
586 e.right.visit_with(self);
587
588 self.in_assign_lhs = old;
589 }
590
591 fn visit_pat(&mut self, pat: &Pat) {
592 match pat {
593 Pat::Ident(i) => {
594 if self.in_var_decl && !self.in_assign_lhs {
596 return;
597 }
598 self.insert(i.id.clone().into());
599 }
600 _ => {
601 pat.visit_children_with(self);
602 }
603 }
604 }
605
606 fn visit_var_declarator(&mut self, var: &VarDeclarator) {
607 let in_var_decl = self.in_var_decl;
608 self.in_var_decl = true;
609
610 if self.in_weak {
611 let ids: Vec<Id> = find_pat_ids(&var.name);
612 self.excluded.extend(ids);
613 }
614
615 var.visit_children_with(self);
616
617 self.in_var_decl = in_var_decl;
618 }
619
620 fn visit_expr(&mut self, expr: &Expr) {
621 match expr {
622 Expr::Ident(i) => {
623 if self.in_var_decl && self.in_assign_lhs {
624 return;
625 }
626 self.insert(i.into());
627 }
628 _ => {
629 expr.visit_children_with(self);
630 }
631 }
632 }
633
634 fn visit_prop(&mut self, prop: &Prop) {
635 match prop {
636 Prop::Shorthand(i) => {
637 self.insert(i.into());
638 }
639 _ => prop.visit_children_with(self),
640 }
641 }
642
643 fn visit_member_expr(&mut self, e: &MemberExpr) {
644 e.obj.visit_with(self);
645
646 if let MemberProp::Computed(c) = &e.prop {
647 c.visit_with(self);
648 }
649 }
650
651 fn visit_super_prop_expr(&mut self, e: &SuperPropExpr) {
652 if let SuperProp::Computed(c) = &e.prop {
653 c.visit_with(self);
654 }
655 }
656}
657
658fn calc_deps(new: &[ModuleItem]) -> StmtDepGraph {
659 tracing::debug!("Analyzing dependencies between statements");
660 let mut graph = StmtDepGraph::default();
661
662 let mut declared_by = FxHashMap::<Id, Vec<usize>>::default();
663 let mut uninitialized_ids = FxHashMap::<Id, usize>::default();
664
665 for (idx, item) in new.iter().enumerate() {
666 graph.add_node(idx);
667
668 match item {
673 ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. }))
675 | ModuleItem::Stmt(Stmt::Decl(decl)) => {
676 match decl {
678 Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
679 declared_by.entry(Id::from(ident)).or_default().push(idx);
681 }
682 Decl::Var(vars) => {
683 for var in &vars.decls {
684 let ids: Vec<Id> = find_pat_ids(&var.name);
686 for id in ids {
687 if var.init.is_none() {
688 uninitialized_ids.insert(id.clone(), idx);
689 }
690
691 declared_by.entry(id).or_default().push(idx);
693 }
694 }
695 }
696 _ => {}
697 }
698 }
699 _ => {}
700 }
701
702 {
703 let mut v = FieldInitFinder::default();
705 item.visit_with(&mut v);
706
707 for id in v.accessed {
708 if let Some(declarator_indexes) = declared_by.get(&id) {
709 for &declarator_index in declarator_indexes {
724 if declarator_index != idx {
725 graph.add_edge(idx, declarator_index, Required::Always);
726 }
732 }
733
734 declared_by.entry(id).or_default().push(idx);
736 }
737 }
738 }
739 }
740
741 for (uninit_id, start_idx) in uninitialized_ids {
748 for (idx, item) in new.iter().enumerate().filter(|(idx, _)| *idx > start_idx) {
749 let mut finder = InitializerFinder {
750 ident: uninit_id.clone(),
751 found: false,
752 in_complex: false,
753 };
754 item.visit_with(&mut finder);
755 if finder.found {
756 declared_by.entry(uninit_id).or_default().push(idx);
757 break;
758 }
759 }
760 }
761
762 for (idx, item) in new.iter().enumerate() {
763 let mut visitor = RequirementCalculator::default();
768
769 item.visit_with(&mut visitor);
770
771 for (id, kind) in visitor.required_ids {
772 if visitor.excluded.contains(&id) {
773 continue;
774 }
775
776 if let Some(declarator_indexes) = declared_by.get(&id) {
777 for &declarator_index in declarator_indexes {
778 if declarator_index != idx {
779 graph.add_edge(idx, declarator_index, kind);
794 if cfg!(debug_assertions) {
799 assert!(graph
800 .neighbors_directed(idx, Dependencies)
801 .any(|x| x == declarator_index));
802 }
803 }
804 }
805 }
806 }
807 }
808
809 graph
810}
811
812#[cfg(test)]
813mod tests {
814 use swc_common::DUMMY_SP;
815 use swc_ecma_ast::*;
816
817 use super::{calc_deps, Dependencies};
818 use crate::{bundler::tests::suite, debug::print_hygiene};
819
820 fn assert_no_cycle(s: &str) {
821 suite().file("main.js", s).run(|t| {
822 let info = t.module("main.js");
823 let module = (*info.module).clone();
824
825 let graph = calc_deps(&module.body);
826
827 for i in 0..module.body.len() {
828 if let ModuleItem::Stmt(Stmt::Decl(Decl::Fn(..))) = &module.body[i] {
829 continue;
830 }
831
832 let deps = graph
833 .neighbors_directed(i, Dependencies)
834 .collect::<Vec<_>>();
835
836 for dep in deps {
837 if let ModuleItem::Stmt(Stmt::Decl(Decl::Fn(..))) = &module.body[dep] {
838 continue;
839 }
840 print_hygiene(
841 "first item",
842 &t.cm,
843 &Module {
844 span: DUMMY_SP,
845 body: vec![module.body[dep].clone()],
846 shebang: None,
847 },
848 );
849 print_hygiene(
850 "second item",
851 &t.cm,
852 &Module {
853 span: DUMMY_SP,
854 body: vec![module.body[i].clone()],
855 shebang: None,
856 },
857 );
858 assert!(
859 !graph.has_a_path(dep, i),
860 "{dep} and {i} is dependant to each other",
861 );
862 }
863 }
864
865 Ok(())
866 });
867 }
868
869 #[test]
870 fn no_cycle_1() {
871 assert_no_cycle(
872 r#"
873 function lexer(str) {
874 const tokens = [];
875 let i = 0;
876 while(i < str.length){
877 const char = str[i];
878 if (char === "*" || char === "+" || char === "?") {
879 tokens.push({
880 type: "MODIFIER",
881 index: i,
882 value: str[i++]
883 });
884 continue;
885 }
886 if (char === "\\") {
887 tokens.push({
888 type: "ESCAPED_CHAR",
889 index: i++,
890 value: str[i++]
891 });
892 continue;
893 }
894 if (char === "{") {
895 tokens.push({
896 type: "OPEN",
897 index: i,
898 value: str[i++]
899 });
900 continue;
901 }
902 if (char === "}") {
903 tokens.push({
904 type: "CLOSE",
905 index: i,
906 value: str[i++]
907 });
908 continue;
909 }
910 if (char === ":") {
911 let name = "";
912 let j = i + 1;
913 while(j < str.length){
914 const code = str.charCodeAt(j);
915 if ((code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || code === 95) {
916 name += str[j++];
917 continue;
918 }
919 break;
920 }
921 if (!name) throw new TypeError(`Missing parameter name at ${i}`);
922 tokens.push({
923 type: "NAME",
924 index: i,
925 value: name
926 });
927 i = j;
928 continue;
929 }
930 if (char === "(") {
931 let count = 1;
932 let pattern = "";
933 let j = i + 1;
934 if (str[j] === "?") {
935 throw new TypeError(`Pattern cannot start with "?" at ${j}`);
936 }
937 while(j < str.length){
938 if (str[j] === "\\") {
939 pattern += str[j++] + str[j++];
940 continue;
941 }
942 if (str[j] === ")") {
943 count--;
944 if (count === 0) {
945 j++;
946 break;
947 }
948 } else if (str[j] === "(") {
949 count++;
950 if (str[j + 1] !== "?") {
951 throw new TypeError(`Capturing groups are not allowed at ${j}`);
952 }
953 }
954 pattern += str[j++];
955 }
956 if (count) throw new TypeError(`Unbalanced pattern at ${i}`);
957 if (!pattern) throw new TypeError(`Missing pattern at ${i}`);
958 tokens.push({
959 type: "PATTERN",
960 index: i,
961 value: pattern
962 });
963 i = j;
964 continue;
965 }
966 tokens.push({
967 type: "CHAR",
968 index: i,
969 value: str[i++]
970 });
971 }
972 tokens.push({
973 type: "END",
974 index: i,
975 value: ""
976 });
977 return tokens;
978 }
979 function parse(str, options = {
980 }) {
981 const tokens = lexer(str);
982 const { prefixes ="./" } = options;
983 const defaultPattern = `[^${escapeString(options.delimiter || "/#?")}]+?`;
984 const result = [];
985 let key = 0;
986 let i = 0;
987 let path = "";
988 const tryConsume = (type)=>{
989 if (i < tokens.length && tokens[i].type === type) return tokens[i++].value;
990 };
991 const mustConsume = (type)=>{
992 const value = tryConsume(type);
993 if (value !== undefined) return value;
994 const { type: nextType , index } = tokens[i];
995 throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}`);
996 };
997 const consumeText = ()=>{
998 let result1 = "";
999 let value;
1000 while((value = tryConsume("CHAR") || tryConsume("ESCAPED_CHAR"))){
1001 result1 += value;
1002 }
1003 return result1;
1004 };
1005 while(i < tokens.length){
1006 const char = tryConsume("CHAR");
1007 const name = tryConsume("NAME");
1008 const pattern = tryConsume("PATTERN");
1009 if (name || pattern) {
1010 let prefix = char || "";
1011 if (prefixes.indexOf(prefix) === -1) {
1012 path += prefix;
1013 prefix = "";
1014 }
1015 if (path) {
1016 result.push(path);
1017 path = "";
1018 }
1019 result.push({
1020 name: name || key++,
1021 prefix,
1022 suffix: "",
1023 pattern: pattern || defaultPattern,
1024 modifier: tryConsume("MODIFIER") || ""
1025 });
1026 continue;
1027 }
1028 const value = char || tryConsume("ESCAPED_CHAR");
1029 if (value) {
1030 path += value;
1031 continue;
1032 }
1033 if (path) {
1034 result.push(path);
1035 path = "";
1036 }
1037 const open = tryConsume("OPEN");
1038 if (open) {
1039 const prefix = consumeText();
1040 const name1 = tryConsume("NAME") || "";
1041 const pattern1 = tryConsume("PATTERN") || "";
1042 const suffix = consumeText();
1043 mustConsume("CLOSE");
1044 result.push({
1045 name: name1 || (pattern1 ? key++ : ""),
1046 pattern: name1 && !pattern1 ? defaultPattern : pattern1,
1047 prefix,
1048 suffix,
1049 modifier: tryConsume("MODIFIER") || ""
1050 });
1051 continue;
1052 }
1053 mustConsume("END");
1054 }
1055 return result;
1056 }
1057 const parse1 = parse;
1058 function compile(str, options) {
1059 return tokensToFunction(parse(str, options), options);
1060 }
1061 const compile1 = compile;
1062 function tokensToFunction(tokens, options = {
1063 }) {
1064 const reFlags = flags(options);
1065 const { encode =(x)=>x
1066 , validate =true } = options;
1067 const matches = tokens.map((token)=>{
1068 if (typeof token === "object") {
1069 return new RegExp(`^(?:${token.pattern})$`, reFlags);
1070 }
1071 });
1072 return (data)=>{
1073 let path = "";
1074 for(let i = 0; i < tokens.length; i++){
1075 const token = tokens[i];
1076 if (typeof token === "string") {
1077 path += token;
1078 continue;
1079 }
1080 const value = data ? data[token.name] : undefined;
1081 const optional = token.modifier === "?" || token.modifier === "*";
1082 const repeat = token.modifier === "*" || token.modifier === "+";
1083 if (Array.isArray(value)) {
1084 if (!repeat) {
1085 throw new TypeError(`Expected "${token.name}" to not repeat, but got an array`);
1086 }
1087 if (value.length === 0) {
1088 if (optional) continue;
1089 throw new TypeError(`Expected "${token.name}" to not be empty`);
1090 }
1091 for(let j = 0; j < value.length; j++){
1092 const segment = encode(value[j], token);
1093 if (validate && !(matches[i]).test(segment)) {
1094 throw new TypeError(`Expected all "${token.name}" to match "${token.pattern}", but got "${segment}"`);
1095 }
1096 path += token.prefix + segment + token.suffix;
1097 }
1098 continue;
1099 }
1100 if (typeof value === "string" || typeof value === "number") {
1101 const segment = encode(String(value), token);
1102 if (validate && !(matches[i]).test(segment)) {
1103 throw new TypeError(`Expected "${token.name}" to match "${token.pattern}", but got "${segment}"`);
1104 }
1105 path += token.prefix + segment + token.suffix;
1106 continue;
1107 }
1108 if (optional) continue;
1109 const typeOfMessage = repeat ? "an array" : "a string";
1110 throw new TypeError(`Expected "${token.name}" to be ${typeOfMessage}`);
1111 }
1112 return path;
1113 };
1114 }
1115 const tokensToFunction1 = tokensToFunction;
1116 function match(str, options) {
1117 const keys = [];
1118 const re = pathToRegexp(str, keys, options);
1119 return regexpToFunction(re, keys, options);
1120 }
1121 const match1 = match;
1122 function regexpToFunction(re, keys, options = {
1123 }) {
1124 const { decode =(x)=>x
1125 } = options;
1126 return function(pathname) {
1127 const m = re.exec(pathname);
1128 if (!m) return false;
1129 const { 0: path , index } = m;
1130 const params = Object.create(null);
1131 for(let i = 1; i < m.length; i++){
1132 if (m[i] === undefined) continue;
1133 const key = keys[i - 1];
1134 if (key.modifier === "*" || key.modifier === "+") {
1135 params[key.name] = m[i].split(key.prefix + key.suffix).map((value)=>{
1136 return decode(value, key);
1137 });
1138 } else {
1139 params[key.name] = decode(m[i], key);
1140 }
1141 }
1142 return {
1143 path,
1144 index,
1145 params
1146 };
1147 };
1148 }
1149 const regexpToFunction1 = regexpToFunction;
1150 function escapeString(str) {
1151 return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1");
1152 }
1153 function flags(options) {
1154 return options && options.sensitive ? "" : "i";
1155 }
1156 function regexpToRegexp(path, keys) {
1157 if (!keys) return path;
1158 const groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g;
1159 let index = 0;
1160 let execResult = groupsRegex.exec(path.source);
1161 while(execResult){
1162 keys.push({
1163 name: execResult[1] || index++,
1164 prefix: "",
1165 suffix: "",
1166 modifier: "",
1167 pattern: ""
1168 });
1169 execResult = groupsRegex.exec(path.source);
1170 }
1171 return path;
1172 }
1173 function arrayToRegexp(paths, keys, options) {
1174 const parts = paths.map((path)=>pathToRegexp(path, keys, options).source
1175 );
1176 return new RegExp(`(?:${parts.join("|")})`, flags(options));
1177 }
1178 function stringToRegexp(path, keys, options) {
1179 return tokensToRegexp(parse(path, options), keys, options);
1180 }
1181 function tokensToRegexp(tokens, keys, options = {
1182 }) {
1183 const { strict =false , start =true , end =true , encode =(x)=>x
1184 } = options;
1185 const endsWith = `[${escapeString(options.endsWith || "")}]|$`;
1186 const delimiter = `[${escapeString(options.delimiter || "/#?")}]`;
1187 let route = start ? "^" : "";
1188 for (const token of tokens){
1189 if (typeof token === "string") {
1190 route += escapeString(encode(token));
1191 } else {
1192 const prefix = escapeString(encode(token.prefix));
1193 const suffix = escapeString(encode(token.suffix));
1194 if (token.pattern) {
1195 if (keys) keys.push(token);
1196 if (prefix || suffix) {
1197 if (token.modifier === "+" || token.modifier === "*") {
1198 const mod = token.modifier === "*" ? "?" : "";
1199 route += `(?:${prefix}((?:${token.pattern})(?:${suffix}${prefix}(?:${token.pattern}))*)${suffix})${mod}`;
1200 } else {
1201 route += `(?:${prefix}(${token.pattern})${suffix})${token.modifier}`;
1202 }
1203 } else {
1204 route += `(${token.pattern})${token.modifier}`;
1205 }
1206 } else {
1207 route += `(?:${prefix}${suffix})${token.modifier}`;
1208 }
1209 }
1210 }
1211 if (end) {
1212 if (!strict) route += `${delimiter}?`;
1213 route += !options.endsWith ? "$" : `(?=${endsWith})`;
1214 } else {
1215 const endToken = tokens[tokens.length - 1];
1216 const isEndDelimited = typeof endToken === "string" ? delimiter.indexOf(endToken[endToken.length - 1]) > -1 : endToken === undefined;
1217 if (!strict) {
1218 route += `(?:${delimiter}(?=${endsWith}))?`;
1219 }
1220 if (!isEndDelimited) {
1221 route += `(?=${delimiter}|${endsWith})`;
1222 }
1223 }
1224 return new RegExp(route, flags(options));
1225 }
1226 const tokensToRegexp1 = tokensToRegexp;
1227 function pathToRegexp(path, keys, options) {
1228 if (path instanceof RegExp) return regexpToRegexp(path, keys);
1229 if (Array.isArray(path)) return arrayToRegexp(path, keys, options);
1230 return stringToRegexp(path, keys, options);
1231 }
1232 const pathToRegexp1 = pathToRegexp;
1233 "#,
1234 );
1235 }
1236}