Skip to content

Instantly share code, notes, and snippets.

@donmccurdy
Last active December 29, 2022 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save donmccurdy/57d806d3e58584944fc88de8208ce1d3 to your computer and use it in GitHub Desktop.
Save donmccurdy/57d806d3e58584944fc88de8208ce1d3 to your computer and use it in GitHub Desktop.
Example vite/rollup plugin for processing binary files.
import { createFilter, normalizePath } from "@rollup/pluginutils";
import fs from "fs/promises";
import crypto from "crypto";
import path from "path";
const PLUGIN_NAME = "bin";
const HASH_LENGTH = 8;
const DEFAULT_PLUGIN_OPTIONS = {
include: "**/*.{bin}",
exclude: "",
publicPath: "",
transforms: [],
verbose: false,
};
/**
* Import and process .bin assets.
*/
export default function bin(pluginOptions = {}) {
pluginOptions = { ...DEFAULT_PLUGIN_OPTIONS, ...pluginOptions };
const filter = createFilter(pluginOptions.include, pluginOptions.exclude);
/** Whether the plugin is running in Vite, true if configResolved() runs. */
let isVite = false;
/** Public path prepended to asset URLs. Does *not* affect the output location on disk. */
let publicPath = pluginOptions.publicPath;
/** Output location on disk. Inferred from Vite/Rollup configuration. */
let outputPath = "";
return {
name: PLUGIN_NAME,
/* Type: (options: InputOptions) => InputOptions | null */
/* Kind: sync, sequential */
options(options) {},
/* Type: (options: InputOptions) => void */
/* Kind: async, parallel */
async buildStart(options) {
// ... do some initialization ...
},
configResolved({ root, base, build: { outDir, assetsDir } }) {
isVite = true;
publicPath ||= assetsDir;
outputPath = assetsDir;
},
/* Type: (id: string) =>
string |
null |
{ code: string, map?: string | SourceMap, ast?: ESTree.Program, moduleSideEffects?: boolean | null } */
/* Kind: async, first */
async load(id) {
id = path.resolve(normalizePath(id)); // Required by Vite.
if (!filter(id)) return null;
const t0 = performance.now();
const bin = await fs.readFile(id);
const hash = generateHash(bin);
const extname = path.extname(id);
const basename = path.basename(id, extname);
const publicAssetPath = publicPath + "/" + `${basename}.${hash}.bin`;
const outputAssetPath = path.join(outputPath, `${basename}.${hash}.bin`);
this.emitFile({
type: "asset",
fileName: outputAssetPath,
source: processBin(bin),
});
const totalMS = performance.now() - t0;
// Vite logs assets automatically, Rollup doesn't.
if (!isVite) {
console.log(
""
+ green("created ")
+ greenBold(path.join("dist", outputAssetPath))
+ green(" in ")
+ greenBold(Math.round(totalMS) + "ms")
)
}
return `export default new URL("${publicAssetPath}").href;`;
},
// TODO(cleanup): Defer writing to one of these methods?
async writeBundle() {},
async generateBundle() {},
};
}
// Utilities.
function processBin(bin) {
// ... do some processing ...
return bin;
}
function generateHash(bin) {
const hash = crypto.createHash("sha1");
hash.update(bin);
return hash.digest("hex").substring(0, HASH_LENGTH);
}
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return "0 Bytes";
const k = 1000;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
}
const _reset = "\x1b[0m";
const _green = "\x1b[32m";
const _greenBold = "\x1b[1;32m";
const green = (text) => `${_green}${text}${_reset}`;
const greenBold = (text) => `${_greenBold}${text}${_reset}`;
@donmccurdy
Copy link
Author

I'm not planning to update this gist over time, but see https://github.com/nytimes/rd-bundler-3d-plugins for something that should remain up to date for processing binary files in Vite and Rollup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment