Skip to content
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

Unified withParams utility to replace individual path, query, and host utilities #447

Merged
merged 13 commits into from
Jan 30, 2025

Conversation

stackoverfloweth
Copy link
Contributor

We ship separate utilities for assigning params for each of the different parts of the url. For the most part these all worked exactly the same (Hash was a little different, more on that later). This PR introduces a single, unified utility withParams to replace them all.

import { withParams } from '@kitbag/router'

const events = createRoute({
  name: 'events',
  path: withParams('/events/[year]/[?month]', {
    year: Number,
    month: withDefault(monthParam, 'january'),
  }),
  query: withParams('enabled=[?enabled]', {
    enabled: true,
  }),
})

I really didn't like the fact that hash didn't quite fit but still had extremely similar types. The biggest difference is that hash stored the value as string | undefined, but the others always store string. Hence our empty paths being defined as Path<'', {}>. I actually prefer the idea of undefined so withParams stores value as possibly undefined. It has an overload that takes zero arguments and a separate property toString: () => string for instances where we need to normalize to a string.

Additionally, I decided to support params in the hash because it works exactly like path and I assumed it would be a small change. It's a bit bigger than I expected but still small enough that I feel like it was the right call.

Hash also had some logic for stripping a leading "#" when it's created but I just moved that so it would work like the other utilities.

To be kind to our current user base I've exported path, query, and host utilities but marked them as deprecated.

@stackoverfloweth stackoverfloweth self-assigned this Jan 28, 2025
Copy link

netlify bot commented Jan 28, 2025

Deploy Preview for kitbag-router ready!

Name Link
🔨 Latest commit b25e1c8
🔍 Latest deploy log https://app.netlify.com/sites/kitbag-router/deploys/679bc99c0f913e00085099f6
😎 Deploy Preview https://deploy-preview-447--kitbag-router.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

src/services/createExternalRoute.spec.ts Show resolved Hide resolved
src/services/createRoute.spec-d.ts Show resolved Hide resolved

return {
...getPathParams(route.host, `${protocol}//${host}`),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pretty sure this is a bug in production right now, host advertises support for params but doesn't validate them when matching

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be getHostParams?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we reuse "path" functions, rename without the word "path".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

investigate if we actually need a "query" version as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose not to duplicate all the tests from path, host, query, etc since the specific formats aren't expected to make a difference

src/services/combineHash.spec.ts Outdated Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the combinePath naming doesn't make sense now that its used for all the different pieces. Its also kinda interesting that since it accepts WithParams that there's no type safety preventing us from accidentally doing WithParams<TParent['path'], TQuery> 🤔

Copy link
Contributor Author

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 have that protection today, but it might be worth exploring. Something like assigning an internal kind (tagged type) to ensure wires don't get crossed

src/services/combinePath.ts Show resolved Hide resolved
? WithParams<CombinePathString<TParentPath, TChildPath>, RemoveLeadingQuestionMarkFromKeys<TParentParams> & RemoveLeadingQuestionMarkFromKeys<TChildParams>>
: WithParams<undefined, {}>
: WithParams<undefined, {}>
: WithParams<undefined, {}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we gain something from the undefined vs {} change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we just assume hash '' (empty string) is the same as undefined we could remove all the toStrings(). See if you can remember why hash was special and expected undefined separately from empty string

src/services/createExternalRoute.ts Outdated Show resolved Hide resolved
src/services/index.ts Outdated Show resolved Hide resolved

return {
...getPathParams(route.host, `${protocol}//${host}`),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be getHostParams?


return optionalParams.length - missing.length
}

export function countExpectedQueryParams(route: Route, actual: QuerySource): number {
const actualQuery = new URLSearchParams(actual)
const expectedQuery = new URLSearchParams(route.query.value)
const expectedQuery = new URLSearchParams(route.query.toString())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we go back to having a toString method? Just for my own understanding.

src/services/urlAssembly.ts Show resolved Hide resolved
src/services/withParams.ts Outdated Show resolved Hide resolved
@pleek91 pleek91 changed the title With params utility Unified withParams utility to replace individual path, query, and host utilities Jan 30, 2025
@stackoverfloweth stackoverfloweth merged commit 8c0f10e into main Jan 30, 2025
6 checks passed
@stackoverfloweth stackoverfloweth deleted the withParams-utility branch January 30, 2025 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants