Thinkmill / keystatic

First class CMS experience, TypeScript API, Markdown & YAML/JSON based, no DB
https://keystatic.com
MIT License
1.23k stars 79 forks source link

Enhancement: more configurable collection path #340

Open florian-lefebvre opened 1 year ago

florian-lefebvre commented 1 year ago

Current state

Currently, the path is always static except for the slug, specified by slugField.

The problem

Projects often require more custom paths, e.g.: locales, publish date

Proposal

Make the collection config an intersection like so (pseudocode):

type Collection = { /* base props */}
    & (
        {
            path: string // with * validation
            slugField: keyof fields 
        }
        | {
            path: (fields: Record<string, any>) => string // with * validation
            slugField: keyof fields // might not be required, we could manually specify the field by interpolation instead
        }
    )

Examples

Locales

export default defineConfig({
    // ...
    collections: {
        blog: collection({
            label: "Blog",
            path: ({ locale }) => `src/content/blog/${locale}/*`
            schema: {
                // ...
                locale: fields.text({ label: "Locale" }) // could be a relationship, whatever
            }
        })
    }
})

Publish date

export default defineConfig({
    // ...
    collections: {
        blog: collection({
            label: "Blog",
            path: ({ publishDate }) => `src/content/blog/${publishDate}.*`
            schema: {
                // ...
                publishDate: fields.date({ label: "Published date" })
            }
        })
    }
})
JedWatson commented 1 year ago

Noted on the use case here 👍

We're pretty keen on keeping slugs static (i.e not having a function you need to call in order to know where entries live) because we need to load the content directly from GitHub over their API, and knowing that as part of static config is very helpful. The challenge with allowing a function to define it is that it becomes non-deterministic, while that's not the case in your examples above, there'd be nothing stopping you from doing:

path: () => `/content/${date.now()}/*`

... chaos 💥

However we've discussed supporting support multiple slugFields as an alternative, which may solve the problem:

export default defineConfig({
    // ...
    collections: {
        blog: collection({
            // ...
            slugFields: ['locale', 'slug'],
            path: 'src/content/blog/*/*', // stars are replaced with values in order of the keys in the array above
            schema: {
                // ...
                slug: fields.slug(/* ... */),
                locale: fields.text({ label: "Locale" })
            }
        })
    }
})

What do you think? sound useful? can you see any limitations with this approach, if we added it, that you couldn't work around?

florian-lefebvre commented 1 year ago

Sounds good 👍, a few thoughts:

Right now, path requires either * or ** to be included. By setting the slugFields, we could pass them to the path type to force including [locale] and [slug] in the path. I don't know if this is possible though 🤔.

Wdyt?

JedWatson commented 1 year ago

I had a chat with the team today about it, and there's some nuance that we need to solve, but I've put it on the roadmap.

I like your idea about using the name of the field in the path too, we'll see what we can come up with and whether that's possible 🙂

Can you expand a bit more on what you mean by "it should be applied for usage with document fields" too?

florian-lefebvre commented 1 year ago

Awesome thanks! My bad, I just checked my schema and I thought putting format: { contentField: 'document' } changed the key to set for the path. Nevermind!

florian-lefebvre commented 1 year ago

Hey @JedWatson, hope you're doing well! Do you have any ETA about this? Or could I help in any way, maybe with some guidance? It has become a must for the project I'm working on and I can dedicate a bit of time to make changes to keystatic

florian-lefebvre commented 1 year ago

Sounds good 👍, a few thoughts:

  • It should be also applied for usage with documents fields
  • Maybe we could push this a bit further with Typescript string interpolation

Right now, path requires either * or ** to be included. By setting the slugFields, we could pass them to the path type to force including [locale] and [slug] in the path. I don't know if this is possible though 🤔.

Wdyt?

@JedWatson have a look at https://www.typescriptlang.org/play#code/C4TwDgpgBAxgFhGBrACgQ2HAPAZQDYCuA5lBAB7AQB2AJgM5R3ABOAllUQDRQAqpF1eoxbsiAPigBeKAG18xALr9KtBjIBQUKPJLkVQtFRBQA-FAAUfPYIYADACQBvQyAC+9pzvfOjr26e1CEgAuKCoIADcIZgBKKFDwqOZ1BU0AnjSEyOiAbnV1GAB7KiZYZggMCABhQrw8RGBWYqkoXCDlG2E2Dm50TA7VLtExczS6IIAxVgg8elCdGQVONLAMOFD4RFQ1tuJetbF1GNCIwtYaKQlHVzyC4tKyFphyypq6hqaqcxkAIjQf7g-ABGPyWUB+eEK5QAtgBaVhgOgEaEAensaBRADNCoUgWhmD8YnkgA

Credits to SuperKXT on Matt's TS Wizards Discord server

EDIT: more realistic version https://www.typescriptlang.org/play#code/C4TwDgpgBAxgFhGBrACgQ2HAPAZQDYCuA5lBAB7AQB2AJgM5R3ABOAllUQDRQAqpF1eoxbsiAPigBeKAG18xALr9KtBjIBQUKPJLkVQtFRBQA-FAAUfPYIYADACQBvQyAC+9pzvfOjr26e1CEgAuKCoIADcIZgBKKFDwqOZ1BU0AnjSEyOiAbnV1UEgoAGEAezw8RGBWUqpcIOUbYTYObnRMRtVm0QlpRzS6IIAxVgg8elCdGQU8rTAMOFD4RFQF+uI2hbE81zz1GFqmWGYIDAgyiqqaqikodd0BLqYWrih2uE6hZ57zNIPLmDVWqhC6VQHXe6bTBidRxSQSf5goFUPYHKhHMi3GAnM6gq61cyORjDUbjOihGQAIholIU3HmmFClPsNKgrhiewAZgQqODasdTpQylROawiFg0joGNYugAlRClZg0LDfVrdDhiThpd7Sx5CeUHJVYJAQEClTmBYh0biq8RarR4vnoz4MfpaLQyJBQdhQE1mi1ShShImDYgjMYTS1EOiehTTHJQBmLN4LGNIJS7NKudRicxo0VEYN-cpI67kqBu92e703P3mkol-HooOyNLu2AIZDvLCO5FpuOU0NEcNk2kyKgEAC2ACNonSGwC+7GqUnaTD20oZUJx9lmKl21ozCGSRHy72y8vByfRzNEwsskk2W33SDG07+7M2Ts4pWTsACMwNz5mKOz5GiRzGNI2KCuctQFoSaQAPSIVoAB6JjFouZZFu2aDBsSYakpGVI0vOSZMiylJsva7rTksOJCm+yKEgRw5EeWVIQLS9L3lAzJcWyMQ0a4WrsnkQA

algora-pbc commented 1 year ago

💎 $100 bounty created by @florian-lefebvre 👉 To claim this bounty, submit your pull request on Algora 📝 Before proceeding, please make sure you can receive payouts in your country 💵 Payment arrives in your account 2-5 days after the bounty is rewarded 💯 You keep 100% of the bounty award 🙏 Thank you for contributing to Thinkmill/keystatic!

aazam-gh commented 1 year ago

Hi is this issue still available to be worked on? Would love to give it a shot :)

florian-lefebvre commented 1 year ago

cc @JedWatson

aazam-gh commented 1 year ago

@florian-lefebvre any updates?

florian-lefebvre commented 1 year ago

nope, I'm not on the team so waiting for an answer as well

sravanth-space commented 1 year ago

@algora-pbc Can you add a bounty label for issue?

florian-lefebvre commented 1 year ago

That's a bot, I think labelling has to be done by the keystatic team if they wish

rishi-raj-jain commented 1 year ago

@florian-lefebvre

Is this open to work? Would love to crush this.

reteps commented 3 weeks ago

@JedWatson Any updates?