compilePath function
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());
}