+ );
+ }
+
+ let renderer: TestRenderer.ReactTestRenderer;
+ TestRenderer.act(() => {
+ renderer = TestRenderer.create(
+
+
+ } />
+
+
+ );
+ });
+
+ expect(() =>
+ TestRenderer.act(() => {
+ renderer.root.findAllByType("button")[0].props.onClick();
+ })
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"Cannot include a '?' character in a manually specified \`to.pathname\` field [{\\"pathname\\":\\"/about/thing?search\\"}]. Please separate it out to the \`to.search\` field. Alternatively you may provide the full path as a string in and the router will parse it for you."`
+ );
+
+ expect(() =>
+ TestRenderer.act(() => {
+ renderer.root.findAllByType("button")[1].props.onClick();
+ })
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"Cannot include a '#' character in a manually specified \`to.pathname\` field [{\\"pathname\\":\\"/about/thing#hash\\"}]. Please separate it out to the \`to.hash\` field. Alternatively you may provide the full path as a string in and the router will parse it for you."`
+ );
+
+ expect(() =>
+ TestRenderer.act(() => {
+ renderer.root.findAllByType("button")[2].props.onClick();
+ })
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"Cannot include a '?' character in a manually specified \`to.pathname\` field [{\\"pathname\\":\\"/about/thing?search#hash\\"}]. Please separate it out to the \`to.search\` field. Alternatively you may provide the full path as a string in and the router will parse it for you."`
+ );
+
+ expect(() =>
+ TestRenderer.act(() => {
+ renderer.root.findAllByType("button")[3].props.onClick();
+ })
+ ).toThrowErrorMatchingInlineSnapshot(
+ `"Cannot include a '#' character in a manually specified \`to.search\` field [{\\"pathname\\":\\"/about/thing\\",\\"search\\":\\"?search#hash\\"}]. Please separate it out to the \`to.hash\` field. Alternatively you may provide the full path as a string in and the router will parse it for you."`
+ );
+ });
+
describe("with state", () => {
it("adds the state to location.state", () => {
function Home() {
diff --git a/packages/router/utils.ts b/packages/router/utils.ts
index cb3e89b85d..9b593cd1bf 100644
--- a/packages/router/utils.ts
+++ b/packages/router/utils.ts
@@ -768,6 +768,22 @@ function resolvePathname(relativePath: string, fromPathname: string): string {
return segments.length > 1 ? segments.join("/") : "/";
}
+function getInvalidPathError(
+ char: string,
+ field: string,
+ dest: string,
+ path: Partial
+) {
+ return (
+ `Cannot include a '${char}' character in a manually specified ` +
+ `\`to.${field}\` field [${JSON.stringify(
+ path
+ )}]. Please separate it out to the ` +
+ `\`to.${dest}\` field. Alternatively you may provide the full path as ` +
+ `a string in and the router will parse it for you.`
+ );
+}
+
/**
* @private
*/
@@ -777,7 +793,26 @@ export function resolveTo(
locationPathname: string,
isPathRelative = false
): Path {
- let to = typeof toArg === "string" ? parsePath(toArg) : { ...toArg };
+ let to: Partial;
+ if (typeof toArg === "string") {
+ to = parsePath(toArg);
+ } else {
+ to = { ...toArg };
+
+ invariant(
+ !to.pathname || !to.pathname.includes("?"),
+ getInvalidPathError("?", "pathname", "search", to)
+ );
+ invariant(
+ !to.pathname || !to.pathname.includes("#"),
+ getInvalidPathError("#", "pathname", "hash", to)
+ );
+ invariant(
+ !to.search || !to.search.includes("#"),
+ getInvalidPathError("#", "search", "hash", to)
+ );
+ }
+
let isEmptyPath = toArg === "" || to.pathname === "";
let toPathname = isEmptyPath ? "/" : to.pathname;