compilePath function

RegExp compilePath(
  1. String path, {
  2. bool isMount = false,
})

Compiles a route path into a matcher.

A {name} segment matches one segment, {name|pattern} matches one segment against its own pattern, and {*name} swallows the rest of the path. When isMount is set the path is a prefix and everything below it matches, which is what hands a subtree to another handler.

Implementation

RegExp compilePath(String path, {bool isMount = false}) {
  final pattern = StringBuffer('^');
  var afterCatchAll = false;

  for (final segment in _segments(path)) {
    if (afterCatchAll) {
      throw ArgumentError('a catch-all must be the last segment of "$path"');
    }

    pattern.write('/');
    pattern.write(switch (segment) {
      LiteralSegment(:final text) => RegExp.escape(text),
      ParameterSegment() => '([^/]+)',
      ConstrainedSegment(:final pattern) => '($pattern)',
      CatchAllSegment() => '(.*)',
    });
    afterCatchAll = segment is CatchAllSegment;
  }

  pattern.write(switch (_tailOf(path, pattern.length == 1, isMount)) {
    // A mount owns everything below its prefix, and at the root that is every
    // path. `/?` alone matched only the bare root, which made `mount('/', ...)`
    // — the way a single-page build is served — answer 404 for every deep link
    // and every asset.
    _PathTail.rootMount => '/?.*',
    _PathTail.root => '/',
    _PathTail.subtree => '(?:/.*)?',
    _PathTail.trailingSlash => '/',
    _PathTail.exact => '',
  });

  pattern.write(r'$');
  return RegExp(pattern.toString());
}