-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
feat: remix optional segments #4706
feat: remix optional segments #4706
Conversation
🦋 Changeset detectedLatest commit: 85801f1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Hi @lordofthecactus, Welcome, and thank you for contributing to Remix! Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once. You may review the CLA and sign it by adding your name to contributors.yml. Once the CLA is signed, the If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at [email protected]. Thanks! - The Remix team |
Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳 |
Awesome. @brophdawg11 is 1.8.0-pre on React Router 6.4? If so let's merge this and add it to 1.9. Otherwise we'd need to wait until we're on 6.4. EDIT: Just checked, we're not on 6.4 in dev yet. Any reason for this? Technically it should be fine? |
@ryanflorence Yep! Documenting what we discussed here. We do want to do an isolated
So I think we want to merge this after we cut the release branch for |
|
||
// Optional segment routes | ||
["(routes)/$", "routes?/*"], | ||
["(routes)/($)", "routes?/*?"], // TODO: Fails, do we want to allow this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to support optional splats as they're already optional. path="child/*"
will match both /child
and /child/any/other/path
: See https://codesandbox.io/s/optional-splat-segments-xpn3kq
Maybe we throw an error on ($)
syntax?
["(routes)/($)", "routes?/*?"], // TODO: Fails, do we want to allow this? | ||
["(routes)/(sub)/$", "routes?/sub?/*"], | ||
["(routes).(sub)/$", "routes?/sub?/*"], | ||
["(routes.sub)/$", "routes?/sub?/*"], // TODO: Fails, do we want to allow this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I don't think we need to add the complexity of being able to interpolate dots inside an optional segment - the line above is how I'd expect optional segments + dot-notation to work together. In this case I'd expect the generated React Router path to be <Route path="routes.sub?/*">
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
["(routes.sub)/$", "routes?/sub?/*"] // do we want to allow this?
The word "routes" there is confusing to me, so I'm changing it to "products.all"
for my comment 😅
["(products.all)/$", "products?/all?/*"] // do we want to allow this?
No, we don't. The React Router version won't have a way of "grouping" two segments as optional together either:
// this is only for "sub"
<Route path="products/all?" />
// this is for both
<Route path="products?/all?" />
So in Remix it should be the same, only individual segments can be optional:
(products).(all).tsx -> products?/all?
["(nested)/($slug)", "nested?/:slug?"], | ||
["(flat).($slug)", "flat?/:slug?"], | ||
["flat.(sub)", "flat/sub?"], | ||
["(nested)/(index)", "nested?"], // Fails with `"flat?/index?"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need to do an "optional" index route in react router since ?
is only for use with path
. Optional segments are for when you want to use the same element for multiple <Route>
paths, but that wouldn't make sense for index routes:
// You wouldn't need to do this since `/nested` already renders <Nested /> and
// therefore doesn't need the index route to add it
<Route path="nested" element={<Nested />}>
<Route index element={<Nested />} />
<Route path="child" element={<Child />} />
</Parent/>
// So you would just do and the `Outlet` inside of `<Nested/>` would be a no-op
// when you were at `/nested`
<Route path="nested" element={<Nested />}>
<Route path="child" element={<Child />} />
</Parent/>
I haven't ever tried this but I assume if you want an actual /index
URL you would escape it like routes/nested/[index].tsx
? Lets' test that - and if that's the case, would we need to support /routes/nested/([index]).tsx
to output <Route path="nested/index?">
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, []
are the escape characters to escape any of our file conventions. Should make sure it works with ()
, also.
// match literal paren characters in the URL
routes/[(foo)].tsx => <Route path="(foo)" />
// match literal `index` in an optional segment
routes/funds/([index]).tsx => <Route path="index?" />
["(nested)/__layout/($slug)", "nested?/:slug?"], | ||
["($slug[.]json)", ":slug.json?"], | ||
["(sub)/([sitemap.xml])", "sub?/sitemap.xml?"], | ||
["(sub)/[(sitemap.xml)]", "sub?/sitemap.xml?"], // TODO should this have been sub?/(sitemap.xml) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I think this looks like a bug since the []
escaping isn't working right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Working now!
["(sub)/[(sitemap.xml)]", "sub?/sitemap.xml?"], // TODO should this have been sub?/(sitemap.xml) | ||
["(posts)/($slug)/([image.jpg])", "posts?/:slug?/image.jpg?"], | ||
[ | ||
"($[$dollabills]).([.]lol)[/](what)/([$]).($)", // TODO outputs ":$dollabills?/.lol?/what?/$?/:?" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is an escaped [/]
allowed? That seems odd to me. And then we don't need to support ($)
so I think this test could become something like:
[
"($[$dollabills]).([.]lol)/(what)/([$]).($up)",
":$dollabills?/.lol?/what?/$?/:up?"
]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added!
], | ||
["(sub).([[])", "sub?/[?"], | ||
["(sub).(])", "sub?/)?"], | ||
["(sub).([([])])", "sub/[]"], // Fails with "sub?/([)]?" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤯 uhhh - I guess the intention here is sub?/([])?
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems from before there is not a clear handling of multiple brackets within brackets. Either you close on the first match or get the longest within multiple separators? feels like this should be a different PR and discussion.
😭 |
@sergiodxa don't forget, you can make your own convention in remix.config.routes and use |
@ryanflorence @sergiodxa interestingly question mark is still possible on route creation 🤔 |
Co-authored-by: Pedro Cattori <[email protected]>
@brophdawg11 so if we merge this before Remix uses RR 6.5, then optional routes won't work, correct? So what's the rational of merging this prior to that? I get the it won't break things since we could just not tell anyone about optional routes until Remix is on RR 6.5, but then we'd have to remember to write release notes for it later. Why not just merge this after Remix uses RR 6.5? |
Yeah that would break. I'm not advocating for merging prior to that - step 4 in #4706 (comment) encompasses merging this PR and updating |
We can actually merge this before the router dep is updated, only the documentation needs to be delayed. IF somebody uses the syntax they'll get route paths that don't work, that's all. Which you can already do in remix config I'd say merge it so that it's done. I'll add an issue to document it in the release where it works. |
If we do merge it now, might be best to edit the changeset to make it clear that this feature isn't ready for use yet |
I'm going to update the changeset to clear that^ up in a separate PR. |
BTW: Are optional segments a router thing? Or do I need to add support for that in the flat routes convention? |
They are a router thing but we need any route files compilation methods to compile a file-name-syntax ( |
I see. Thanks. So as long as I generate the correct route path, I don't have to worry about expanding the optional routes to multiple routes, that's done by the router, correct? |
I've added optional routes support to flat routes: <Routes>
<Route file="root.tsx">
<Route file="routes-flat/_public.tsx">
<Route path="optional?" file="routes-flat/_public.(optional).tsx" />
<Route path="about" file="routes-flat/_public.about.tsx" />
<Route path="contact.jpg" file="routes-flat/_public.contact[.jpg].tsx" />
</Route>
<Route index file="routes-flat/index.tsx" />
<Route path="test/*" file="routes-flat/test.$.tsx" />
<Route path="users" file="routes-flat/users.tsx">
<Route path="/:userId" file="routes-flat/users.$userId.tsx" />
<Route path="/:userId/edit" file="routes-flat/users.$userId_.edit.tsx" />
</Route>
</Route>
</Routes> |
* fix(remix-dev): convert `config.appDirectory` to relative unix path (#4709) * fix(remix-dev): convert appDirectory to unix style for fast-glob Signed-off-by: Logan McAnsh <[email protected]> * chore: relative path Signed-off-by: Logan McAnsh <[email protected]> * chore: add test Signed-off-by: Logan McAnsh <[email protected]> * fix test Signed-off-by: Logan McAnsh <[email protected]> * fix: typo Signed-off-by: Logan McAnsh <[email protected]> * chore: update test Signed-off-by: Logan McAnsh <[email protected]> Signed-off-by: Logan McAnsh <[email protected]> * chore: add changeset for #4709 (#4718) * ci: add typechecking for deno (#4715) * ci: add typechecking for deno * ci: install deno for integration tests * chore: format * chore: format * fix: Firefox LiveReload (#4725) Firefox infinitely reloads the page as long as `<LiveReload>` is rendering. Closes #4692 * fix(remix-dev): allow defining multiple routes for the same route module file (#3970) * Allow multiple routes for same route module * Update packages/remix-dev/config/routes.ts Co-authored-by: Andrew Leedham <[email protected]> * Update routes.ts - Better name for automated ID variable; - Small adjust in `id` option comment; * - Removing redundant IF * Update routes.ts Revert complex custom ID in routes * Non unique custom routes ID error and test * Update assets.ts Trying to solve a conflict * Revert "Update assets.ts" This reverts commit 2064c57. * Error on collisions with non-custom routeIds * Create big-spoons-grab.md Co-authored-by: Andrew Leedham <[email protected]> Co-authored-by: Matt Brophy <[email protected]> * feat: Allow pass-through script props in `ScrollRestoration` (#2879) * ci(nightly): add deno for typechecking deno files (#4738) * ci: fix race condition writing globals.d.ts shim (#4717) Co-authored-by: Chance Strickland <[email protected]> * chore: bump @playwright/test to latest (#4749) Signed-off-by: Logan McAnsh <[email protected]> Signed-off-by: Logan McAnsh <[email protected]> * Fix 4199: TypedResponse allows incompatible types (#4734) * Fixes #4199: Do not allow assignment of incompatible TypedResponses * Add myself to contributors.yml * Create light-sheep-give.md * slight changeset tweak * additional changeset tweaks Co-authored-by: Pedro Cattori <[email protected]> * chore: format * test: add transition integration tests (#4739) test: useTransition to wait for states This approach could probably be applied across other flakey tests and could also be documented as a good approach of if there is user-feedback for a specific action when running integration tests. * feat: testing helpers (#4539) Co-authored-by: James Restall <[email protected]> Signed-off-by: Logan McAnsh <[email protected]> * chore: format * chore(remix-testing): update dependencies (#4756) * fix(remix-testing): fix deps (#4757) * Fix deps for new remix-testing package * fix lint * Remove ENABLE_REMIX_ROUTER flags (#4732) * chore: add `@remix-run/testing` to changesets (#4781) * chore: add `@remix-run/testing` to changesets Signed-off-by: Logan McAnsh <[email protected]> * chore: update `ADDING_A_PACKAGE.md` Signed-off-by: Logan McAnsh <[email protected]> Signed-off-by: Logan McAnsh <[email protected]> * refactor(remix-react): upgrade Remix to `[email protected]` (non-data-router) and drop `history` (#4731) * Bump remix to [email protected] (#4668) Co-authored-by: Mehdi Achour <[email protected]> * Bump remix to RR 6.4.4 and drop history dependency (#4702) Co-authored-by: Mehdi Achour <[email protected]> * ci(nightly): move git operations after build (#4797) * ci(nightly): move git operations after build when adding deno typechecking (#4715), nightly now refers to the version we just calculated which hasnt been published when trying to build and typecheck Signed-off-by: Logan McAnsh <[email protected]> * ci: update tmp branch name Signed-off-by: Logan McAnsh <[email protected]> Signed-off-by: Logan McAnsh <[email protected]> * perf(remix-dev): Optimize `parentRouteId` lookup in `defineConventionalRoutes` (#4538) * Use object for parentRouteId lookup * Sign CLA * Move parentRouteId logic to a separate function * Update test fixture path * chore: format * chore(dev): add changeset for PR #4538 (#4800) * chore: format * refactor(remix-react): use `react-router-dom` import instead of `react-router` (#3325) * chore: manually bump remix-testing version due to not being on main just yet Signed-off-by: Logan McAnsh <[email protected]> * fixup! chore: manually bump remix-testing version due to not being on main just yet * fixup! chore: manually bump remix-testing version due to not being on main just yet Signed-off-by: Logan McAnsh <[email protected]> * chore: unify error usage (#4696) * chore: format * Add fetcher state/type tests (#4803) * chore(deps): bump esbuild to latest (#4754) * chore: add invariant instead of using `!` Signed-off-by: Logan McAnsh <[email protected]> * chore(deps): bump esbuild to latest Signed-off-by: Logan McAnsh <[email protected]> * Create fresh-shrimps-join.md Signed-off-by: Logan McAnsh <[email protected]> Co-authored-by: Pedro Cattori <[email protected]> * test(integration): close server synchronously (#4785) * chore: normalize afterAll Signed-off-by: Logan McAnsh <[email protected]> * test: close server synchronously Signed-off-by: Logan McAnsh <[email protected]> * chore: appFixture.close isnt async anymore Signed-off-by: Logan McAnsh <[email protected]> * Update integration/helpers/create-fixture.ts Co-authored-by: Pedro Cattori <[email protected]> Signed-off-by: Logan McAnsh <[email protected]> Co-authored-by: Pedro Cattori <[email protected]> * feat: remix optional segments (#4706) * feat: transform optional routes from remix to react router * Add to contributors * Add changeset * fix(optional-segments): fix escaping of parenthesis * small function fix * Update packages/remix-dev/__tests__/routesConvention-test.ts Co-authored-by: Pedro Cattori <[email protected]> Co-authored-by: Pedro Cattori <[email protected]> * chore: format * chore: edit the optional segments changeset (#4815) to make it clear that Remix won't support optional segments until integrated with React Router 6.5 * chore: format * docs: rearrange docs, give everything its own page (#4821) * chore: format * fix: wrong parentheses in the optional segments changeset (#4825) * fix: wrong parentheses in the optional segments changeset * add: akamfoad to the contributers * ci(nightly): add workflow_call for testing purposes Signed-off-by: Logan McAnsh <[email protected]> * Revert "ci(nightly): add workflow_call for testing purposes" This reverts commit f3fa77e. * chore(scripts): Use relaxed peer dependencies to avoid triggering major version bumps (#4736) - Add script to bump peer deps with changesets version Co-authored-by: Mateusz Burzyński <[email protected]> * Add integration tests for request structures (#4829) * fix(remix-dev): resolve asset entry full path to support mono-repo import of styles (#4855) * chore: have eslint report unused eslint comments (#4863) * chore: have eslint report unused eslint comments Signed-off-by: Logan McAnsh <[email protected]> * chore: remove additional comment Signed-off-by: Logan McAnsh <[email protected]> * perf(remix-architect,remix-netlify): improve performance of `isBinaryType` (#4761) Co-authored-by: Logan McAnsh <[email protected]> Co-authored-by: Pannatier Guillaume <[email protected]> * chore: format * chore(remix-testing): remove internal installGlobals (#4755) * chore: remove internal installGlobals Signed-off-by: Logan McAnsh <[email protected]> * chore: add README Signed-off-by: Logan McAnsh <[email protected]> * Create quick-cats-fix.md * Update .changeset/quick-cats-fix.md Co-authored-by: Michaël De Boey <[email protected]> * chore(deps): remove jsdom and happydom from devDependencies Signed-off-by: Logan McAnsh <[email protected]> Signed-off-by: Logan McAnsh <[email protected]> Co-authored-by: Michaël De Boey <[email protected]> * fix(dev): build js modules for ts->js conversion The TS->JS migration was removed from the CLI codemod options, but still used for TS->JS conversion when creating a new Remix project from the CLI. The TS modules responsible for the TS->JS conversion were incorrectly removed from the Rollup build, resulting in the corresponding built JS modules being absent. That caused the CLI to error when trying to perform TS->JS conversion. This changes reintroduces the wiring to build the modules responsible for the TS->JS conversion. Fixes #4854 Signed-off-by: Logan McAnsh <[email protected]> Co-authored-by: Logan McAnsh <[email protected]> Co-authored-by: Matt Kane <[email protected]> Co-authored-by: Remix Run Bot <[email protected]> Co-authored-by: Chance Strickland <[email protected]> Co-authored-by: Ryan Florence <[email protected]> Co-authored-by: Lucas Ferreira <[email protected]> Co-authored-by: Andrew Leedham <[email protected]> Co-authored-by: Matt Brophy <[email protected]> Co-authored-by: dabdine <[email protected]> Co-authored-by: Jacob Ebey <[email protected]> Co-authored-by: James Restall <[email protected]> Co-authored-by: Michaël De Boey <[email protected]> Co-authored-by: Mehdi Achour <[email protected]> Co-authored-by: Dylan Markow <[email protected]> Co-authored-by: Daniel Rios <[email protected]> Co-authored-by: Akam Foad <[email protected]> Co-authored-by: Mateusz Burzyński <[email protected]> Co-authored-by: Guillaume Pannatier <[email protected]> Co-authored-by: Pannatier Guillaume <[email protected]>
Closes: remix-run/react-router#9550
Related: remix-run/react-router#9650
As discussed this would transform remix routes
/($lang)/about
into
/:lang?/about
which would then be matched by react-router (remix-run/react-router#9650)Another example
/(one)/($two)/(three).($four)
file routing would get transformed into `/one?/:two?/three?/:four? which then would get matched by react-router:Context
Parenthesis was chosen to denote optionality since windows file systems can't have
?
Feedback needed
left some tests failing, for some edge cases. I would like feedback on how would be best to handle them or how to be more defensive about this.
Testing Strategy:
This test covers this code: https://github.com/remix-run/remix/compare/dev...lordofthecactus:remix:feat/remix-optional-segments?expand=1#diff-04792fa211911bb758e39db1e8bc1dfffb55657840b8f9f17fbc2330d7b346e1R41-R79