lukeed / trouter

:fish: A fast, small-but-mighty, familiar fish...errr, router*
MIT License
634 stars 23 forks source link

Remove pattern from URL in USE method #20

Closed voxsoftware closed 1 year ago

voxsoftware commented 1 year ago

Hi. There is no way to transform the URL after USE method. Suppose this example:

trouter.use("/book/:id?", function(){})

let route = trouter.find("/book/first/edit")
// you get an object: {params: {id: "first"}, handlers: ...}  but how get the "matched string" ?

In this example, I need to transform: /book/first/edit into /edit but there is no way since, I don't know the portion of URL matched by pattern. How can I do this?

Maybe can be added another field to trouter.find on USE. Something like this:

return {
    params: {
        "id": "first"
    },
    handlers: [fn],
    matched: "/book/first"
}

or maybe:

return {
    params: {
        "id": "first"
    },
    handlers: [fn],
    url: "/edit"
}
lukeed commented 1 year ago

You can override use or find with your own definitions so long as they accept/return the same value shapes.

The problem is that you will have multiple routes (especially with use() involvement) that match different lengths, and because Trouter is agnostic to what you're actually doing, there's no right answer for when or where the "matched portions" should be prioritized over one another. In other words, .use("/foo/*") and .use("/foo/bar/*") and .get("/foo/bar/baz") all match "/foo/bar/baz" but will have different "matched" and/or "remaining" portions.

Here's one rough example of what you could do while still maintaining flexibility. (This wouldn't go into core Trouter since, as you can see, it's user-specific behavior, not necessary to core, and can be added)

import parse from 'regexparam';

class Custom extends Trouter {
  find(method, url) {
    let isHEAD=(method === 'HEAD');
    let i=0, j=0, k, tmp, arr=this.routes;
    let match, matches=[], params={}, handlers=[];
    for (; i < arr.length; i++) {
      tmp = arr[i];
      if (tmp.method.length === 0 || tmp.method === method || isHEAD && tmp.method === 'GET') {
        if (tmp.keys === false) {
          match = tmp.pattern.exec(url);
          if (match === null) continue;
          matches.push(match[0]); // < here
          if (match.groups !== void 0) for (k in match.groups) params[k]=match.groups[k];
          tmp.handlers.length > 1 ? (handlers=handlers.concat(tmp.handlers)) : handlers.push(tmp.handlers[0]);
        } else if (tmp.keys.length > 0) {
          match = tmp.pattern.exec(url);
          if (match === null) continue;
          matches.push(match[0]); // < here
          for (j=0; j < tmp.keys.length;) params[tmp.keys[j]]=match[++j];
          tmp.handlers.length > 1 ? (handlers=handlers.concat(tmp.handlers)) : handlers.push(tmp.handlers[0]);
        } else if (tmp.pattern.test(url)) {
          matches.push(tmp.pattern.exec(url)[0]); // < here
          tmp.handlers.length > 1 ? (handlers=handlers.concat(tmp.handlers)) : handlers.push(tmp.handlers[0]);
        }
      } // else not a match
    }

    return { params, handlers, matches };
  }
}

Then as you loop thru handlers, the same matches[index] corresponds to that handler