muell
muell

Reputation: 563

Nuxt 3: Read Contents of /public at Build Time

Inside the /public/images directory of my Nuxt 3 project, I have a number of images, which are spread out into multiple sub-directories. For a gallery page, I want to read all file paths so that I can render an img tag for each one and don't have to hard-code them. Currently, I do this inside an API endpoint. The code for that looks like this:

import { readdir, stat } from "node:fs";
import { join } from "node:path";

async function read(path, callback) {
  let images = [];

  readdir(path, (err, files) => {
    if (err) {
      throw err;
    }

    let i = 0;

    (function next() {
      if (i === files.length) {
        return callback(images);
      }

      const file = join(path, files[i++]);

      stat(file, (err, stats) => {
        if (err) {
          throw err;
        }

        if (stats.isDirectory()) {
          read(file, (value) => {
            images = images.concat(value);

            next();
          });
        } else {
          images.push(file);

          next();
        }
      });
    })();
  });
}

export default defineEventHandler(async (_event) => {
  return await new Promise((resolve, reject) => {
    try {
      read("./public/images/blog", (value) => {
        resolve(value.map((value) => value.substring(6)));
      });
    } catch (error) {
      reject(error);
    }
  });
});

What I would like to do instead is create this array containing all image paths at build time. From my understanding, this could be achieved using lifecycle hooks. However, I don't understand the documentation for this part of the framework. Can somebody point me in the right direction?

Upvotes: 0

Views: 1158

Answers (1)

kess
kess

Reputation: 1319

Edit: You can use an async runtimeConfig

nuxt.config.ts

import { getImages } from './imageHandler';

export default defineNuxtConfig(async () => {
    return {
        runtimeConfig: {
            public: {
                images: await getImages()
            }
        }
    }
})

MyComponent.vue

<script setup lang="ts">
let config = useRuntimeConfig();
let images = config.public.images;
</script>

<template>
  <img v-for="img in images" :src="img" />
</template>

Note: If you use TypeScript the compiler will complain but the code should run. Use type any for the function to silence the compiler. There are no type declarations because this an undocumented deprecated feature that only exists for backwards compatibility with nuxt 2

Note 2: If you're on a nuxt 3 version that doesn't have this feature (either because it hasn't been added yet or because it has already been removed), you can rewrite your filesystem calls to their synchronous counterparts (fs.readdirSync etc.) and still achieve the same functionality

Note 3: Use runtimeConfig if you only need the data on the server. Use runtimeConfig.public if you need it on the client as well.

See: https://github.com/nuxt/framework/pull/1612 for more info about async function as nuxt.config

See: https://nuxt.com/docs/migration/runtime-config#runtime-config for more info about runtimeConfig

Old answer

You can define a module and use the 'build:before' lifecycle hook to execute code at build time.

Read more about lifecycle hooks here: https://nuxt.com/docs/guide/going-further/hooks

See also: https://nuxt.com/docs/guide/going-further/modules

Although after you have obtained your array of images I'm not sure how you can reference that array from other parts of the code, but I hope this is enough to help you in the right direction.

modules/yourModule.ts

import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    nuxt.hook('build:before', async ()=>{
        // read contents of public and get images
        let images = [];
        // do something with images
    })
  }
})

nuxt.config.ts

import { getProjects } from "./data"
import yourModule from "./modules/yourModule"

export default defineNuxtConfig({
    modules: [
        yourModule 
    ]
})

Upvotes: 1

Related Questions