Skip to content

Commit

Permalink
Add support for ctx.params in getStaticProps/getServerSideProps (verc…
Browse files Browse the repository at this point in the history
…el#3696)

Dynamic segments should be passed a single string, while catch-all segments should be passed a list of strings.

Furthermore, ctx.params was previously undefined because we weren't passing it forward through the render options.
  • Loading branch information
alexkirsz authored Feb 13, 2023
1 parent 6ccab84 commit 0c3a3ee
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 14 deletions.
2 changes: 1 addition & 1 deletion crates/next-core/js/src/internal/api-server-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Handler = (data: {
request: IncomingMessage;
response: ServerResponse<IncomingMessage>;
query: string;
params: Record<string, string>;
params: Record<string, string | string[]>;
path: string;
}) => Promise<void>;

Expand Down
1 change: 1 addition & 0 deletions crates/next-core/js/src/internal/page-server-handler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export default function startHandler({
previewModeEncryptionKey: "",
previewModeSigningKey: "",
},
params: renderData.params,
basePath: "",
// TODO(WEB-583) this isn't correct, instead it should set `dev: true`
nextExport: true,
Expand Down
2 changes: 1 addition & 1 deletion crates/next-core/js/types/turbopack.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextParsedUrlQuery } from "next/dist/server/request-meta";

export type RenderData = {
params: Record<string, string>;
params: Record<string, string | string[]>;
method: string;
url: string;
path: string;
Expand Down
51 changes: 42 additions & 9 deletions crates/next-core/src/next_route_matcher/path_regex.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use turbo_tasks::primitives::{BoolVc, Regex};
use turbopack_node::route_matcher::{ParamsVc, RouteMatcher};
use turbopack_node::route_matcher::{Param, ParamsVc, RouteMatcher};

/// A regular expression that matches a path, with named capture groups for the
/// dynamic parts of the path.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct PathRegex {
regex: Regex,
named_params: Vec<String>,
named_params: Vec<NamedParam>,
}

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
struct NamedParam {
name: String,
kind: NamedParamKind,
}

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
enum NamedParamKind {
Single,
Multi,
}

impl std::fmt::Display for PathRegex {
Expand All @@ -27,12 +39,24 @@ impl RouteMatcher for PathRegex {
self.named_params
.iter()
.enumerate()
.filter_map(|(idx, name)| {
if name.is_empty() {
.filter_map(|(idx, param)| {
if param.name.is_empty() {
return None;
}
let value = capture.get(idx + 1)?;
Some((name.to_string(), value.as_str().to_string()))
Some((
param.name.to_string(),
match param.kind {
NamedParamKind::Single => Param::Single(value.as_str().to_string()),
NamedParamKind::Multi => Param::Multi(
value
.as_str()
.split("/")
.map(|segment| segment.to_string())
.collect(),
),
},
))
})
.collect()
}))
Expand All @@ -42,7 +66,7 @@ impl RouteMatcher for PathRegex {
/// Builder for [PathRegex].
pub struct PathRegexBuilder {
regex_str: String,
named_params: Vec<String>,
named_params: Vec<NamedParam>,
}

impl PathRegexBuilder {
Expand Down Expand Up @@ -74,7 +98,10 @@ impl PathRegexBuilder {
"([^?]+)?"
});
self.push_str(&regex::escape(rem.as_ref()));
self.named_params.push(name.into());
self.named_params.push(NamedParam {
name: name.into(),
kind: NamedParamKind::Multi,
});
}

/// Pushes a catch all segment to the regex.
Expand All @@ -88,7 +115,10 @@ impl PathRegexBuilder {
}
self.push_str("([^?]+)");
self.push_str(&regex::escape(rem.as_ref()));
self.named_params.push(name.into());
self.named_params.push(NamedParam {
name: name.into(),
kind: NamedParamKind::Multi,
});
}

/// Pushes a dynamic segment to the regex.
Expand All @@ -102,7 +132,10 @@ impl PathRegexBuilder {
}
self.push_str("([^?/]+)");
self.push_str(&regex::escape(rem.as_ref()));
self.named_params.push(name.into());
self.named_params.push(NamedParam {
name: name.into(),
kind: NamedParamKind::Single,
});
}

/// Pushes a static segment to the regex.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect } from "react";

export default function Home({ params }: { params: any }) {
useEffect(() => {
// Only run on client
import("@turbo/pack-test-harness").then(() => runTests(params));
});

return <div>Test</div>;
}

export function getServerSideProps(ctx: { params: any }) {
return {
props: {
params: ctx.params,
},
};
}

function runTests(params: any) {
describe("catch-all segments", () => {
it("should be passed a param array", () => {
expect(params.segments).toEqual(["first", "second"]);
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function getServerSideProps() {
return {
redirect: {
destination: "/first/second",
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect } from "react";

export default function Home({ params }: { params: any }) {
useEffect(() => {
// Only run on client
import("@turbo/pack-test-harness").then(() => runTests(params));
});

return <div>Test</div>;
}

export function getServerSideProps(ctx: { params: any }) {
return {
props: {
params: ctx.params,
},
};
}

function runTests(params: any) {
describe("catch-all segments", () => {
it("should be passed a param array", () => {
expect(params.segment).toEqual("dynamic-segment");
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function getServerSideProps() {
return {
redirect: {
destination: "/dynamic-segment",
},
};
}
4 changes: 2 additions & 2 deletions crates/turbopack-node/src/render/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};

use crate::{ResponseHeaders, StructuredError};
use crate::{route_matcher::Param, ResponseHeaders, StructuredError};

pub mod issue;
pub mod node_api_source;
Expand All @@ -12,7 +12,7 @@ pub mod rendered_source;
#[turbo_tasks::value(shared)]
#[serde(rename_all = "camelCase")]
pub struct RenderData {
params: IndexMap<String, String>,
params: IndexMap<String, Param>,
method: String,
url: String,
raw_query: String,
Expand Down
11 changes: 10 additions & 1 deletion crates/turbopack-node/src/route_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@ use indexmap::IndexMap;
use turbo_tasks::primitives::BoolVc;

#[turbo_tasks::value(transparent)]
pub struct Params(Option<IndexMap<String, String>>);
#[derive(Debug, Clone)]
#[serde(untagged)]
pub enum Param {
Single(String),
Multi(Vec<String>),
}

#[turbo_tasks::value(transparent)]
#[derive(Debug, Clone)]
pub struct Params(Option<IndexMap<String, Param>>);

/// Extracts parameters from a URL path.
#[turbo_tasks::value_trait]
Expand Down

0 comments on commit 0c3a3ee

Please sign in to comment.