vite-plugin-foundryvtt
Version:
Plugin for building for foundryvtt. Enables hmr and manifest substitution.
191 lines (185 loc) • 5.73 kB
JavaScript
;
const fs = require('node:fs');
const path = require('node:path');
const foundryvttCli = require('@foundryvtt/foundryvtt-cli');
const classicLevel = require('classic-level');
const vite = require('vite');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
const path__default = /*#__PURE__*/_interopDefaultCompat(path);
async function isLocked(dest) {
try {
const db = new classicLevel.ClassicLevel(dest);
await db.open();
await db.close();
} catch {
return true;
}
return false;
}
function foundryvtt(manifest, {
substitutions = {},
type = "system",
packDir = "src/packs",
buildPacks = true,
packOptions = { yaml: true }
} = {}) {
const config = { substitutions, type, packDir, buildPacks, packOptions };
return [
foundryWriteManifest(manifest, config),
foundryCssFilename(),
foundryVirtualEntryPoint(manifest),
foundryBuildPacks(manifest, config),
foundryHMR(manifest)
];
}
function foundryWriteManifest(manifest, {
substitutions = {},
type = "system"
} = {}) {
for (const [k, v] of Object.entries(substitutions))
if (v === void 0) delete substitutions[k];
return {
name: "foundryvtt-write-manifest",
generateBundle() {
const data = vite.mergeConfig(manifest, substitutions);
const source = JSON.stringify(data, null, 2);
this.emitFile({ type: "asset", fileName: `${type}.json`, source });
}
};
}
function foundryCssFilename() {
return {
name: "foundryvtt-css-filename",
apply: "build",
config(cfg) {
cfg.base = "./";
cfg.build ??= {};
cfg.build.rolldownOptions ??= {};
cfg.build.rolldownOptions.output ??= {};
const afn = {
assetFileNames: ({ names: [name] }) => name?.endsWith(".css") ? `styles/${name}` : name ?? "assets/[name]-[hash][extname]"
};
if (Array.isArray(cfg.build.rolldownOptions.output)) {
cfg.build.rolldownOptions.output.push(afn);
} else {
cfg.build.rolldownOptions.output = vite.mergeConfig(
cfg.build.rolldownOptions.output,
afn
);
}
return cfg;
}
};
}
function foundryVirtualEntryPoint(manifest) {
let entryPoint;
return {
name: "foundry-virtual-entrypoints",
apply(config, { command }) {
return !!config.build?.lib && command === "serve";
},
configResolved(config) {
const lib = config.build?.lib;
if (!lib) return;
entryPoint = (Array.isArray(lib.entry) ? lib.entry.at(0) : typeof lib.entry === "string" ? lib.entry : Object.entries(lib.entry).at(0)?.at(0)) ?? "";
},
async resolveId(source, _importer, { isEntry }) {
const esmEntry = manifest.esmodules?.at(0);
const cssEntry = (() => {
const style = manifest.styles?.at(0);
return typeof style === "string" ? style : style?.src;
})();
if (esmEntry && source === `/${esmEntry}` || isEntry) {
return { id: "\0virtual:entrypoint" };
}
if (cssEntry && source.startsWith(`/${cssEntry}`)) {
return { id: "\0virtual:styles" };
}
},
load(id) {
if (id === "\0virtual:entrypoint") {
return { code: `
import "./${entryPoint}";
` };
}
if (id === "\0virtual:styles") {
return { code: "" };
}
}
};
}
function foundryBuildPacks(manifest, {
packDir = "src/packs",
buildPacks = true,
packOptions = { yaml: true }
} = {}) {
let outdir;
return {
name: "foundryvtt-build-packs",
apply(_config, { command }) {
return buildPacks && command === "build" && (manifest.packs?.length ?? 0) !== 0;
},
async configResolved(cfg) {
outdir = cfg.build.outDir;
for (const pack of manifest.packs ?? []) {
if (!buildPacks) break;
const dest = path__default.resolve(
outdir,
pack.path ?? path__default.resolve("packs", pack.name)
);
if (await isLocked(dest)) buildPacks = false;
}
},
async writeBundle() {
const packs = manifest.packs?.map((p) => ({
...p,
src: path__default.resolve(packDir, p.name),
dest: path__default.resolve(outdir, p.path ?? path__default.resolve("packs", p.name))
}));
if (!buildPacks || !packs) return;
for (const p of packs) {
try {
await foundryvttCli.compilePack(p.src, p.dest, packOptions);
this.info(`Built pack ${p.name}`);
} catch (e) {
if (e instanceof Error) this.warn(e);
}
}
buildPacks = false;
}
};
}
function foundryHMR(manifest) {
const hotReload = manifest.flags?.hotReload || null;
const extensions = hotReload?.extensions.map((e) => e);
const paths = hotReload?.paths?.map((e) => e) ?? [""];
let outdir;
let pubdir;
return {
name: "foundryvtt-hmr",
configResolved(cfg) {
outdir = cfg.build.outDir;
pubdir = cfg.publicDir;
},
handleHotUpdate({ file }) {
if (!hotReload || !extensions) return;
if (!file.startsWith(pubdir)) return;
const file_path = path__default.relative(pubdir, file);
const ext = path__default.extname(file_path).slice(1);
if (!extensions.includes(ext) || !paths.some((p) => file_path.startsWith(p)))
return;
const dest = path__default.resolve(outdir, file_path);
const k = {
css: "css",
json: "language",
html: "template",
hbs: "template",
"": ""
};
console.log(`Hot updating ${k[ext.toLowerCase()]} file '${file_path}'`);
fs__default.copyFileSync(file, dest);
}
};
}
module.exports = foundryvtt;