Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optional group with param compile #142

Open
LKay opened this issue Mar 6, 2018 · 14 comments
Open

Optional group with param compile #142

LKay opened this issue Mar 6, 2018 · 14 comments

Comments

@LKay
Copy link

LKay commented Mar 6, 2018

There are some issues: #95, #106 but I'm not sure if that is the same case.

I want to achieve something like optional group matching based on presence of a parameter and being able to compile path back.

I'd like to define a path like:

(/first/:one)?/second

And the first segment will be returned only if :one parameter is present:

const toPath = pathToRegexp.compile("(/first/:one)?/second");

toPath(); // returns `/second`
toPath({ one : 1 }); // returns `/first/1/second`

Ideally I'd like to have some nested groups:

const toPath = pathToRegexp.compile("(/first/:one(/second/:two)?)?/three");

toPath(); // returns `/three`
toPath({ two : 2 }); // returns `/three` because outer group "first" is not matched
toPath({ one : 1 }); // returns `/first/1/three`
toPath({ one : 1, two: 2 }); // returns `/first/1/second/2/three`

Is this even possible using this library?

@blakeembrey
Copy link
Member

blakeembrey commented Mar 7, 2018

No, it's not possible like that. You can't embed params inside a matching group, it's a regexp expected inside there. It could be possible to consider this in future releases, but right now the only way to do it is to create two separate paths and use an array or use a regexp.

@blakeembrey
Copy link
Member

blakeembrey commented Mar 7, 2018

Another idea would be to introduce nested groups that support paths instead of regexps and may solve a couple of other issues people are struggling with also. Something like:

{/first/:one{/second/:two}}/three

The parts inside {} are paths instead of regexps. To support this, however, we'd need to solve the same problem as #95 which will likely entail rewriting the parser to be an actual parser instead of regexps.

@LKay
Copy link
Author

LKay commented Mar 7, 2018

Fair enough, thanks for replying.

Yeah, introducing {} for defining groups is a nice idea!

First extract groups then perform usual regex conversion to whatever is inside {} and finally compile it all together by flattening extracted groups from the most nested one.

@delijah
Copy link

delijah commented Mar 29, 2018

Actually with version 0.1.7 this has worked. Check here: http://forbeslindesay.github.io/express-route-tester/

@blakeembrey
Copy link
Member

blakeembrey commented Mar 30, 2018

@delijah I'm not sure anyone is arguing against that, but 0.1.x had a host of other bugs that resulted from being able to write paths like this. It was never an official feature, there was a lot of things you could do with it because it was extremely lax.

@delijah
Copy link

delijah commented Dec 19, 2018

Any progress on this?

@blakeembrey
Copy link
Member

blakeembrey commented Nov 11, 2019

@delijah The best way to get progress on this is to submit a PR. I don't think the feature is particularly clear on possible edge cases right now (probably clearer once someone implements it) and it will need enough test coverage to ensure it works correctly.

@blakeembrey
Copy link
Member

blakeembrey commented Nov 12, 2019

I tried implementing this, but one unclear feature is how compile works - from this path, how would we get a valid string back? What is the expected output?

@Tofandel
Copy link

@blakeembrey For the path (/first/:one)?/:second this is the expected output
{second: 'foo'} => /foo
{one: 'bar', second: 'foo'} => /first/bar/foo

@blakeembrey
Copy link
Member

blakeembrey commented Jul 30, 2020

@Tofandel That example is the really only the tip of the iceberg, what about {/first/:one?}? or other nested groupings? If one is missing, does the child still get compiled? The proposal needs a solution for both parsing and compiling. If someone wants to put together such a proposal complete with edge cases, I can review implementing it.

I think I can make an attempt now that we refactored the parser though, and for now make the assumption OP does - if the first isn’t filled then nested gets ignored.

@Tofandel
Copy link

Tofandel commented Jul 30, 2020

{/first/:one?}? is equivalent to (/first)?/:one? so it would compile to this
(/first/:one?)?/:second
{second: 'foo'} => compiles to /foo => also parses /first/foo
{one: 'bar', second: 'foo'} => /first/bar/foo

Regarding nesting it would work as you'd expect

(/first/:one(/nested/:nested))?/:second
{one: 'bar', second: 'foo'} => /foo //nested is required so first part doesn't match
{one: 'bar', nested: 'baz', second: 'foo'} => /first/bar/nested/baz/foo

(/first/:one(/nested/:nested)?)?/:second
{one: 'bar', second: 'foo'} => /first/bar/foo //nested is optional so first part matches

That's really all the edge cases I see

@blakeembrey
Copy link
Member

Cool, feel free to submit a PR! Maybe some more will come up when you try implementing it, I recall running into issues when I actually tried to code it but that was also over two years back now.

@idler8
Copy link

idler8 commented Feb 8, 2023

const toPath1 = pathToRegexp.compile("/three");
const toPath2 = pathToRegexp.compile("/first/:one/three");
const toPath3 = pathToRegexp.compile("/first/:one/second/:two/three");

function toPathGroup() {
  const pathObject = { ...args };
  if (Object.hasOwnProperty("two")) return toPath3(pathObject);
  if (Object.hasOwnProperty("one")) return toPath2(pathObject);
  return toPath1(pathObject);
}

are you need toPathGroup?

@idler8
Copy link

idler8 commented Feb 8, 2023

No, I don't need it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants