content icon indicating copy to clipboard operation
content copied to clipboard

Allow one file sources

Open Tahul opened this issue 2 years ago • 7 comments

We currently only support specifying a directory as a custom source like this:

{
   name: 'fa-ir',
   prefix: '/fa', // All contents inside this source will be prefixed with `/fa`
   driver: 'fs',
   base: resolve(__dirname, 'content-fa') // Path for source directory
}

Would it be possible to support specifying a complete file path as a source?

It would be useful for cases like:

  • Adding nuxt and @nuxt/content dependencies to a repository
  • Only have a nuxt.config.ts at the root of the project
  • Render a single file like README.md with a custom theme as a website for the repository

Tahul avatar Jul 11 '22 16:07 Tahul

cc @farnabaz

Tahul avatar Jul 11 '22 16:07 Tahul

Well from what I see you want to have the README.md treated as an index.md ?

atinux avatar Jul 11 '22 17:07 atinux

Yes, pointing to an absolute file path would add the file as / path from that source.

Maybe we could preserve the file name for the _id and other generated keys though.

Tahul avatar Jul 11 '22 17:07 Tahul

For this, we need to create a specific driver to handle the single file. It should be able to handle a single file from every source, fs, github, ...

farnabaz avatar Jul 12 '22 07:07 farnabaz

There is two way to create single-file drivers for unstorage:

1 - Allow unstorage to expose storage instance into drivers and create a simple alias driver

// simple aliasing
export default defineDriver((_opts: DriverOptions) => {
  const opts = { ..._opts, ...defaultOptions }
  return {
    getKeys () {
      return Promise.resolve([opts.alias as string])
    },
    hasItem (key) {
      return Promise.resolve(key === opts.alias)
    },
    getItem (key) {
      if (key === opts.alias) {
        return storageInstance.getItem(opts.source)
      }
      return Promise.resolve(null)
    },
    setItem (key, value) {
      if (key === opts.alias) {
        return storageInstance.setItem(opts.source, value)
      }
      return Promise.resolve(undefined)
    }
  }
})

Pros

  • With aliases we do not need to create separate drivers for each file source (fs-single, github-single, cloudflare-kv-single, ....)
  • With aliases, we don't need to mount multiple drivers for the same source, For example, we can simple alias root:README.md into content:index.md without the need for any extra fs-driver

Cons

  • The storage instance should expose to drivers, it might not be safe to expose the whole storage to drivers.
  • In some cases, the user might need to define two sources. (for example for reading README.md from GitHub source, she needs to define two sources, one for the GitHub repo and the other for README.md alias

2 - Create a separate single driver for each driver

Pros

  • This approach is straightforward and it does not require any change in unstorage core.

Cons

  • We need to create multiple drivers for this purpose (fs-single, github-single, cloudflare-kv-single, ....)
  • Performance-wise, it is slow. For example, reading content and README.md from github require two drivers that both tries to fetch repo

farnabaz avatar Jul 25 '22 15:07 farnabaz

Thanks a lot for the clear explanation @farnabaz 💚

@pi0 ; what is your opinion on @farnabaz comment? :)

Tahul avatar Jul 25 '22 17:07 Tahul

Content can create a virtual or overlay driver for single soures with a nested unstorage instance. There is a performance side-effect since with single file mounts, we want to avoid listing operations on those mountpoints that this driver can do. I would suggest instead of extending driver key, introduce a new option such as fileName: 'README.md' that enables this behavior.

pi0 avatar Jul 26 '22 12:07 pi0