Paulie
Paulie

Reputation: 2422

How should an npm library put a file in the public folder?

(I have a Vue/Vite application but I think this question is generic to any setup.)

In my app, I have some code that I want to generalize into a separate npm library package so it can be used in multiple apps. This requires placing a file in the public folder of the app but I'm not sure what the best practice way of doing that is.

(This file is a service worker, but I think that is irrelevant to the question - this would be the same for any file that needs to have a public URL.)

I'd like the use of this library to be as clean as possible. That is, I'd like to not tell the client to "copy this file into your public folder".

I think that copy step needs to happen somewhere, though, unless there is a way to tell Vite to add a file to what it packages.

The copy step could be when running npm install, I guess. It would be nice if that could also add a line to .gitignore, too, if needed. I think this is complicated because the actual location is different whether the app is Vue, Nuxt, or some other framework.

I don't think the copy step could be when import mylib from "mylib" because that happens after the app is already packaged.

So, I think my two options are:

  1. Tell Vite to put a particular file, unaltered, in the public folder.

  2. Run a post install script when npm install runs. (That would have to work whether the user is on Windows, mac, or linux.)

  3. Some other magic?

I don't know how to implement any of those, so a pointer to some documentation or a good term to search for would be appreciated, too.

Or is this just a fool's errand and I should tell the user to just copy the file to their public folder manually?

Upvotes: 0

Views: 648

Answers (1)

eten
eten

Reputation: 1104

If you are using Vite, you can create an npm library with a Vite plugin in it. The documentation for Vite plugins is here, and the API that Vite's plugin API stems from is here. The code for a plugin to add to Vite's output and dev server would be

import { writeFile } from "fs";
import { resolve } from "path";

let url = "service-worker.js"
let fileContent = "file contents"
let outPath;

const myPlugin = () => ({
  name: "add-service-worker",
  configResolved(resolvedConfig) {
    outPath = resolve(resolvedConfig.root, resolvedConfig.build.outDir, url);
  },
  configureServer(server) {
    server.middlewares.use(`/${url}`, (req, res, next) => {
      res.writeHead(200, { "Content-Type": "text/javascript" });
      res.write(fileContent);
      res.end();
    });
  },
  closeBundle() {
    writeFile(outPath, fileContent, { flag: "w" }, (error) => {
      if (error) {
        throw error;
      }

      console.log(`Generated file to ${outPath}`);
    });
  },
});

export default myPlugin;

The same could most likely be applied to a rollup or webpack plugin. However, the real easiest way would be to make this a library, and import it using

import service-worker from "service-worker"

Upvotes: 1

Related Questions