Package Exports
- @timberstack/vite-plugin-better-manifest
- @timberstack/vite-plugin-better-manifest/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@timberstack/vite-plugin-better-manifest) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Better Manifest
Vite plugin for an easier backend integration. Please read first the official documentation.
Usage
npm install @timberstack/vite-plugin-better-manifest
pnpm install @timberstack/vite-plugin-better-manifest
deno install npm:@timberstack/vite-plugin-better-manifest
// vite.config.{ts, js}
import { defineConfig } from "vite";
import BetterManifest from "@timberstack/vite-plugin-better-manifest";
export default defineConfig({
plugins: [BetterManifest()],
});
Options
BetterManifest({
resourcesDir: "/resources", // will set your vite root option
buildTarget: "esnext", // build target in case you're using typescript
})
The problem
While vite's backend integration docs is amazing, things can get pretty boilerplate-ish. This is fine, you can get your way out of it with just a couple of minutes of copy/paste. However, the real problem comes when it's time to build for production.
The official documentation provides a sort of pseudo code for explaining how to use the generated manifest.json
for building your final HTML with the corresponding links. Personally, I find this a bit confusing and hard to get it right (and that's why there are already so many integrations already).
The solution
Better Manifest does a couple of interesting things:
Dev mode
It provides the initial configuration needed for the integration to work. Under the hood, it's setting the initial options needed for making use of your assets in the most organic way possible. It sets the vite root to your desired resources folder (by default /resources
) so they can be accessed directly from there, as if it was your regular public directory.
Going to prod
When it comes to building, it outputs the generated assets
folder to your public directory and the .vite
folder to the root of your project. Inside the .vite
folder you will find the original manifest.json
together with a better-manifest.json
. For the input, it utilizes all the files found in your vite root folder (by default /resources
).
Given the following project structure:
├── public
│ └── favicon.ico # will be preserved
├── resources
│ ├── css
│ │ ├── contact.css
│ │ └── index.css
│ ├── ts
│ │ ├── dynamic.ts
│ │ └── noEntry.ts
│ ├── contact.ts # added to rollup input options
│ └── index.ts # added to rollup input options
When running vite build
, your project will look like this:
├── .vite # Generated by vite, but moved by the plugin
│ ├── better-manifest.json # This is the real deal
│ └── manifest.json
├── public
│ ├── favicon.ico
│ └── assets # Generated by vite
│ ├── contact-CaP_4Zek.css
│ ├── contact-CZqaBrwj.js
│ ├── dynamic-BSHE5Y3H.js
│ ├── index-CD-sq4b9.js
│ ├── index-DdZIYClp.js
│ └── index-W1erjkBN.css
├── resources
│ # ...
The .vite/better-manifest.json
file has the following content:
{
"/contact.ts": "<link rel=\"stylesheet\" href=\"/assets/contact-CaP_4Zek.css\" />\n<script type=\"module\" src=\"/assets/contact-CZqaBrwj.js\"></script>",
"/index.ts": "<link rel=\"stylesheet\" href=\"/assets/index-W1erjkBN.css\" />\n<script type=\"module\" src=\"/assets/index-CD-sq4b9.js\"></script>"
}
So, when going to production, you just grab the original file name from better-manifest.json
and you will have the pre-generated string tags.
It's kind of a plug and play: register the plugin, grab any of the ViteHead
components from the examples
directory and start using your assets served by vite either in dev or prod mode.
Suggested usage
Here's an implementation in javascript on how the vite head component should look like:
import { readFileSync } from "node:fs";
const isProd = Deno.env.get("PROD");
// parsing the manifest
let betterManifest;
if (isProd) {
try {
betterManifest = JSON.parse(
readFileSync("../../.vite/better-manifest.json", { encoding: "utf-8" })
)
} catch (_) {
if (isProd) console.log("Manifest not found. Run the build command.");
}
}
// Vite Head component
export const ViteHead = (script) => {
if(!script.startsWith("/")) {
console.error("Scripts must start with '/'!")
return;
}
if (!script) return;
const devScripts = `
<script type="module" src="http://localhost:5173/@vite/client"></script>
<script type="module" src="http://localhost:5173${script}"></script>
`;
return `
${isProd ? betterManifest[script] : devScripts}
`;
};
// ... somewhere in your code
ViteHead("/index.ts")
Few things to notice:
- You can, of course, move the parsing of the manifest to wherever you feel like (maybe the entry point of your application).
- The expected script name should start with
/
and be relative to your vite root./index.ts
will produce the following urlhttp://localhost:5173/index.ts
, and it works because the root is set to the/resources
folder. If the root of vite was set to the project's directory (not recommended), you should pass the file name with the corresponding prefix (/resources/index.ts
).
License
MIT