optional splat routes (#13547)
Co-authored-by: SteelRazor47 <36779933+SteelRazor47@users.noreply.github.com>
This commit is contained in:
@@ -4,7 +4,11 @@ export function getRoute(file: string): string {
|
||||
const importPath = normalizePath(file)
|
||||
return importPath
|
||||
.replace(/.*\/admin\/(routes)/, "")
|
||||
.replace(/\[([^\]]+)\]/g, ":$1")
|
||||
.replace("[[*]]", "*?") // optional splat
|
||||
.replace("[*]", "*") // splat
|
||||
.replace(/\(([^\[\]\)]+)\)/g, "$1?") // optional static, (foo)
|
||||
.replace(/\[\[([^\]]+)\]\]/g, ":$1?") // optional dynamic, [[foo]]
|
||||
.replace(/\[([^\]]+)\]/g, ":$1") // dynamic, [foo]
|
||||
.replace(
|
||||
new RegExp(
|
||||
`/page\\.(${VALID_FILE_EXTENSIONS.map((ext) => ext.slice(1)).join(
|
||||
|
||||
@@ -42,6 +42,13 @@ type DashboardAppProps = {
|
||||
plugins: DashboardPlugin[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches segments that are optional and at the end of the path.
|
||||
* Example: /path/to/:id?
|
||||
* Such paths can be added to the menu items without the optional segment.
|
||||
*/
|
||||
const OPTIONAL_LAST_SEGMENT_MATCH = /\/([^\/])+\?$/
|
||||
|
||||
export class DashboardApp {
|
||||
private widgets: WidgetMap
|
||||
private menus: MenuMap
|
||||
@@ -129,10 +136,11 @@ export class DashboardApp {
|
||||
allMenuItems.sort((a, b) => a.path.length - b.path.length)
|
||||
|
||||
allMenuItems.forEach((item) => {
|
||||
if (item.path.includes("/:")) {
|
||||
item.path = item.path.replace(OPTIONAL_LAST_SEGMENT_MATCH, "")
|
||||
if (item.path.includes("/:") || item.path.endsWith("/*")) {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.warn(
|
||||
`[@medusajs/dashboard] Menu item for path "${item.path}" can't be added to the sidebar as it contains a parameter.`
|
||||
`[@medusajs/dashboard] Menu item for path "${item.path}" can't be added to the sidebar as it contains a mandatory parameter.`
|
||||
)
|
||||
}
|
||||
return
|
||||
|
||||
@@ -37,9 +37,10 @@ const createBranchRoute = (segment: string): RouteObject => ({
|
||||
const createLeafRoute = (
|
||||
Component: ComponentType,
|
||||
loader?: LoaderFunction,
|
||||
handle?: object
|
||||
handle?: object,
|
||||
path: string = ""
|
||||
): RouteObject => ({
|
||||
path: "",
|
||||
path,
|
||||
ErrorBoundary: ErrorBoundary,
|
||||
async lazy() {
|
||||
const result: {
|
||||
@@ -149,7 +150,6 @@ const addRoute = (
|
||||
|
||||
if (isComponentSegment || remainingSegments.length === 0) {
|
||||
route.children ||= []
|
||||
const leaf = createLeafRoute(Component, loader)
|
||||
|
||||
if (handle) {
|
||||
route.handle = handle
|
||||
@@ -159,9 +159,17 @@ const addRoute = (
|
||||
route.loader = loader
|
||||
}
|
||||
|
||||
leaf.children = processParallelRoutes(parallelRoutes, currentFullPath)
|
||||
route.children.push(leaf)
|
||||
|
||||
// Since splat segments must occur at the end of a route, react-router enforces the segment to be a leaf.
|
||||
// Therefore we can't create a child leaf route with `path: ""` and must instead modify the route itself
|
||||
if (currentSegment === "*?" || currentSegment === "*") {
|
||||
const leaf = createLeafRoute(Component, loader, handle, currentSegment)
|
||||
leaf.children = processParallelRoutes(parallelRoutes, currentFullPath)
|
||||
Object.assign(route, leaf)
|
||||
} else {
|
||||
const leaf = createLeafRoute(Component, loader)
|
||||
leaf.children = processParallelRoutes(parallelRoutes, currentFullPath)
|
||||
route.children.push(leaf)
|
||||
}
|
||||
if (remainingSegments.length > 0) {
|
||||
addRoute(
|
||||
remainingSegments,
|
||||
|
||||
Reference in New Issue
Block a user