path-to-regexp icon indicating copy to clipboard operation
path-to-regexp copied to clipboard

Return the regexp used for matching

Open ex1st opened this issue 1 year ago • 2 comments

This is an attempt to return the functionality to build a routers tree for express app. Developers can replace layer.regexp by layer.matchers[0].regexp in original code from @dougwilson https://github.com/expressjs/express/discussions/5858#discussioncomment-10436717

https://github.com/expressjs/express/discussions/5961

ex1st avatar Sep 21 '24 08:09 ex1st

While this can be added I’d rather not add it for this reason. I think you’d be better off describing what you actually want as a feature and working on getting that added officially, since that code feels like it could break any time the regex changes and I don’t understand how it would work properly for all the different features of path-to-regexp.

What is it you actually want from Express?

blakeembrey avatar Sep 21 '24 17:09 blakeembrey

Hi @blakeembrey, if https://github.com/pillarjs/router/pull/123 will be merged, i will close it.

What is it you actually want from Express?

List of routes.

ex1st avatar Sep 25 '24 15:09 ex1st

Hi @blakeembrey do you plan to fix it in another way, or have you just closed it?

ex1st avatar Apr 29 '25 18:04 ex1st

As I said above:

I think you’d be better off describing what you actually want as a feature and working on getting that added officially, since that code feels like it could break any time the regex changes and I don’t understand how it would work properly for all the different features of path-to-regexp.

Since you replied that you want a "list of routes", this isn't the right library for that. The regex isn't helpful toward that goal and you would want to try and land this in the router instead.

blakeembrey avatar Apr 29 '25 18:04 blakeembrey

I think it'd also help to be a bit more granular about what a "list of routes" is, or open a PR, or provide some examples in the router library. For example, how should the list handle an array or nested routers? Or a nested router in an array? What's the preferred output format? A flat array, or a nested tree? Do you care about the structure of the application itself in those routes? Should the output format be a string or TokenData? Is regex really the best output format for what people want to do with the list?

Most of this isn't something that would be exposed at this level, but features could be added when the above questions get clarification if we need additional features in the library.

blakeembrey avatar Apr 29 '25 18:04 blakeembrey

Now I'm creating a list of routes using the modified code by @dougwilson, but path-to-regex no longer returns a regex. Next code works but I want to drop the crazy monkey-patching of RegExp.prototype.

function availableRoutes(router) {
	const routes = router.stack
		.filter(r => r.route)
		.map(r => {
			return {
				method: Object.keys(r.route.methods)[0].toUpperCase(),
				path: r.route.path
			};
		});
	const subRouters = router.stack
		.filter(r => r.name === "router");
	for (const subRouter of subRouters) {
		const orig = RegExp.prototype.exec;
		let source;
		RegExp.prototype.exec = function(...args) {
			Object.defineProperty(RegExp.prototype, "exec", {
				value: orig,
				writable: true,
				enumerable: false,
				configurable: true
			});
			source = this.source;
			return this.exec(...args);
		};
		subRouter.matchers[0]();
		source = source.replaceAll(/[$?=(){}\\|^]+/g, "").replaceAll(/:[/]+/g, "/");
		const subroutes = availableRoutes(subRouter.handle);
		for (const sr of subroutes) {
			sr.path = `${source}${sr.path}`.replaceAll(/\/+/g, "/");
			routes.push(sr);
		}
	}
	return routes;
}

to

function availableRoutes(router) {
	const routes = router.stack
		.filter(r => r.route)
		.map(r => {
			return {
				method: Object.keys(r.route.methods)[0].toUpperCase(),
				path: r.route.path
			};
		});
	const subRouters = router.stack
		.filter(r => r.name === "router");
	for (const subRouter of subRouters) {
		const source = subRouter.matchers[0].regexp.replaceAll(/[$?=(){}\\|^]+/g, "").replaceAll(/:[/]+/g, "/");
		const subroutes = availableRoutes(subRouter.handle);
		for (const sr of subroutes) {
			sr.path = `${source}${sr.path}`.replaceAll(/\/+/g, "/");
			routes.push(sr);
		}
	}
	return routes;
}

ex1st avatar Apr 29 '25 18:04 ex1st

I think I understand what you're doing, I still don't really understand the goals though. I think we might be stuck on the XY problem, where you have a specific solution already but it won't really work in a bunch of different cases or for other users.

I'm trying to make assumptions about what you actually want, but I just don't know since I don't have the use-case. .replaceAll(/[$?=(){}\\|^]+/g, "").replaceAll(/:[/]+/g, "/") doesn't seem particularly helpful to me. It just spits out garbage and could break any time I change the regex output.

For example, pathToRegexp('/file/:path{.:ext}').regexp.source.replaceAll(/[$?=(){}\\|^]+/g, "").replaceAll(/:[/]+/g, "/") outputs:

'/file/[/]+.[/.]+/file/[/]+/'

That doesn't seem like what you'd want right?

blakeembrey avatar Apr 29 '25 19:04 blakeembrey