I'm trying to make the paths (slugs? I get confused over terminology sometimes) of my content follow the file structure under the /content directory, but I can't get it quite right. You can see my code at https://codesandbox.io/s/uy5lo?file=/nuxt.config.js. This is built on Nuxt 2.13 with target:static.
This is the behavior I hope to achieve:
directory/file | resulting path
----------------+----------------
/content |
/hello.md | /hello
/foo.md | /foo
/foo |
/bar.md | /foo/bar
...or alternatively, this would be fine too:
directory/file | resulting path
----------------+----------------
/content |
/hello.md | /hello
/foo |
/index.md | /foo
/bar.md | /foo/bar
To make this work, I created a _.vue route which fetches content with the {deep: true}. This seems to work fine. I also added the following hook:
hooks: {
'content:file:beforeInsert': (document) => {
// Just strip the leading slash off document.path.
document.slug = document.path.replace(/^\//, '')
}
}
While this works for /hello and /foo/bar, it does not work for /foo. The foo.md file seems to clash with the directory of the same name. I did read in the documentation that "If path [...] is a directory it will return an Array", which sounds like an explanation, but it doesn't provide a solution.
What did I miss? Thanks for your assistance.
You can not have a file /foo.md and a folder foo. The file will be ignored. Docs schould be changed / explain this behavior.
Thanks @mathe42.
So what about the alternative structure I proposed, with no foo.md but with a foo/index.md? I mean - it works as long as I don't change the content:file:beforeInsert hook and foo/index.md becomes available on /foo/index. But obviously I would need to strip off the /index part to produce the desired slugs, like this:
hooks: {
'content:file:beforeInsert': (document) => {
if (document.path.endsWith('/index')) {
document.slug = document.path.replace(/^\//, '').replace(/\/index$/, '')
} else {
document.slug = document.path.replace(/^\//, '')
}
}
}
...and that's when things break again.
It seems like the rule is that a slug must never be identical to a directory name, because the mechanism which returns the directory content takes precedence. Is that correct? This sounds like a design flaw to me: one function tries to do too much (fetching both items and listings) and ends up getting into a conflict with itself.
Hi, I'm facing the same issue. Are there any guidelines or best practices for nested subfolders? Thanks!
Hi @marcvangend
Could you try with:
const [ doc ] = await $content('', { deep: true }).where({ path: route.path.replace(/\/$/, '') }).fetch()
With this you can also remove your hook.
Hi @benjamincanac, thanks for your suggestion. Although it didn't immediately work for me, you did put me on the right track. More specifically, I didn't realize that the first argument to $content() can be an empty string, allowing you to simply fetch everything and filter from there. (I guess this could be made clearer in the docs.)
I ended up with the following code in _.vue (and indeed no more beforeInsert hook). To be clear, this works with the first file structure I suggested in the original post.
async asyncData ({ $content, params }) {
const path = params.pathMatch || 'index'
const [doc] = await $content('', { deep: true }).where({ path: `/${path}` }).fetch()
return { doc }
}
So can we close this issue? I'm tempted to say yes, because the question in the title has been answered. On the other hand, the solution does feel like a workaround.
The @nuxt/content design implicitly assumes that a file basename (without extension) and a directory name will never the same, which is obviously not true. With this assumption as starting point, and no way for the developer to override it, it builds a query reading from either a single file or an entire directory. IMHO that is the core problem that needs to be fixed.
@marcvangend The first argument of $content can actually be nothing because it defaults to /: https://content.nuxtjs.org/fetching#contentpath-options, so you can do:
await $content({ deep: true }).where({ path: `/${path}` }).fetch()
I agree with you, you should be able to query a file with the same name as a directory, I've added this to our roadmap, thanks for your feedback!
Good tip @benjamincanac, and thanks for adding it to the roadmap. Glad I could be of some assistance.
Most helpful comment
@marcvangend The first argument of
$contentcan actually be nothing because it defaults to/: https://content.nuxtjs.org/fetching#contentpath-options, so you can do:I agree with you, you should be able to query a file with the same name as a directory, I've added this to our roadmap, thanks for your feedback!