vite-plugin-fvtt 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,8 +1,28 @@
1
1
  # Changelog
2
2
 
3
- ## [0.2.2]
3
+ ## [0.2.4] - 2025-09-23
4
4
 
5
- ## Fixed
5
+ ### Changed
6
+
7
+ - Removed dependencies of `fs-extra` and `dotenv` to shrink the dependencies.
8
+ - Async file loading should improve the performance for a large number of language files significantly.
9
+
10
+ ## [0.2.3] - 2025-09-20
11
+
12
+ ### Fixed
13
+
14
+ - Return absolute paths from globbing.
15
+ - Refactor path-utils import and usage across codebase, for better Windows support.
16
+ - Refactor logger to use static Logger class methods.
17
+ - Bump dependencies.
18
+
19
+ ### Added
20
+
21
+ - Add Vitest and fixture-based build test.
22
+
23
+ ## [0.2.2] - 2025-09-10
24
+
25
+ ### Fixed
6
26
 
7
27
  - Improve windows path resolution.
8
28
 
@@ -75,7 +95,8 @@
75
95
 
76
96
  - Initial Release
77
97
 
78
- [unreleased]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.2.2...HEAD
98
+ [unreleased]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.2.3...HEAD
99
+ [0.2.3]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.2.2...v0.2.3
79
100
  [0.2.2]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.2.1...v0.2.2
80
101
  [0.2.1]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.2.0...v0.2.1
81
102
  [0.2.0]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.1.4...v0.2.0
package/README.md CHANGED
@@ -104,7 +104,8 @@ Merging follows your manifest’s declared language paths, searching in root or
104
104
 
105
105
  ### **6. Packs**
106
106
 
107
- Packs are tried to be auto-discovered in the source directory. If the paths match, they are automatically compiled.
107
+ Packs are tried to be auto-discovered in the source directory. If the paths match, they are
108
+ automatically compiled.
108
109
 
109
110
  **Note:** Packs are currently not watched for changes.
110
111
 
package/dist/index.d.ts CHANGED
@@ -3,6 +3,6 @@ import { Plugin } from "vite";
3
3
  //#region src/index.d.ts
4
4
  declare function foundryVTTPlugin(options?: {
5
5
  buildPacks: boolean;
6
- }): Plugin;
6
+ }): Promise<Plugin>;
7
7
  //#endregion
8
8
  export { foundryVTTPlugin as default };
package/dist/index.js CHANGED
@@ -1,8 +1,6 @@
1
- import fs from "fs-extra";
2
- import posix from "path/posix";
3
- import dotenv from "dotenv";
4
1
  import path from "path";
5
- import { globSync } from "tinyglobby";
2
+ import { glob } from "tinyglobby";
3
+ import fs from "fs/promises";
6
4
  import { compilePack } from "@foundryvtt/foundryvtt-cli";
7
5
  import { Server } from "socket.io";
8
6
  import { io } from "socket.io-client";
@@ -10,73 +8,126 @@ import { io } from "socket.io-client";
10
8
  //#region src/context.ts
11
9
  const context = {};
12
10
 
11
+ //#endregion
12
+ //#region src/utils/fs-utils.ts
13
+ var FsUtils = class FsUtils {
14
+ static async checkType(p, check) {
15
+ try {
16
+ const stats = await fs.stat(p);
17
+ return check(stats);
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+ static async fileExists(p) {
23
+ return FsUtils.checkType(p, (s) => s.isFile());
24
+ }
25
+ static async dirExists(p) {
26
+ return FsUtils.checkType(p, (s) => s.isDirectory());
27
+ }
28
+ static async readFile(filePath, encoding = "utf-8") {
29
+ return fs.readFile(filePath, { encoding });
30
+ }
31
+ static async readJson(filePath) {
32
+ try {
33
+ const content = await FsUtils.readFile(filePath);
34
+ return JSON.parse(content);
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+ };
40
+ var fs_utils_default = FsUtils;
41
+
13
42
  //#endregion
14
43
  //#region src/config/env.ts
15
- function loadEnv() {
16
- const envPath = path.resolve(process.cwd(), ".env.foundryvtt.local");
17
- const { parsed } = dotenv.config({
18
- path: envPath,
19
- quiet: true
20
- });
44
+ function parseEnv(content) {
45
+ const result = {};
46
+ for (const line of content.split(/\r?\n/)) {
47
+ const trimmed = line.trim();
48
+ if (!trimmed || trimmed.startsWith("#")) continue;
49
+ const [key, ...rest] = trimmed.split("=");
50
+ result[key.trim()] = rest.join("=").trim();
51
+ }
52
+ return result;
53
+ }
54
+ async function loadEnv() {
55
+ const envPaths = await glob(".env.foundryvtt*", { absolute: true });
56
+ let merged = {
57
+ FOUNDRY_URL: "localhost",
58
+ FOUNDRY_PORT: "30000"
59
+ };
60
+ for (const file of envPaths) {
61
+ const content = await fs_utils_default.readFile(file, "utf-8");
62
+ merged = {
63
+ ...merged,
64
+ ...parseEnv(content)
65
+ };
66
+ }
21
67
  return {
22
- foundryUrl: parsed?.FOUNDRY_URL ?? "localhost",
23
- foundryPort: parseInt(parsed?.FOUNDRY_PORT ?? "30000", 10)
68
+ foundryUrl: merged.FOUNDRY_URL,
69
+ foundryPort: parseInt(merged.FOUNDRY_PORT, 10)
24
70
  };
25
71
  }
26
72
 
27
73
  //#endregion
28
74
  //#region src/utils/logger.ts
29
- var Logger = class {
30
- namespace;
31
- colors = {
32
- info: "\x1B[32m",
75
+ var Logger = class Logger {
76
+ static namespace = "vite-plugin-fvtt";
77
+ static colors = {
78
+ info: "\x1B[36m",
33
79
  warn: "\x1B[33m",
34
80
  error: "\x1B[31m"
35
81
  };
36
- constructor({ namespace = "vite-plugin-fvtt" } = {}) {
37
- this.namespace = namespace;
82
+ static reset = "\x1B[0m";
83
+ initialize(namespace = "vite-plugin-fvtt") {
84
+ Logger.namespace = namespace;
38
85
  }
39
- format(level, message) {
40
- const color = this.colors[level] ?? "";
41
- const reset = "\x1B[0m";
42
- return `${color}[${this.namespace}] [${level.toUpperCase()}]${reset} ${message}`;
86
+ static format(level, message) {
87
+ return `${Logger.colors[level] ?? ""}[${Logger.namespace}] [${level.toUpperCase()}]${Logger.reset} ${message}`;
43
88
  }
44
- info(message) {
45
- console.log(this.format("info", message));
89
+ static info(message) {
90
+ console.log(Logger.format("info", message));
46
91
  }
47
- warn(message) {
48
- console.warn(this.format("warn", message));
92
+ static warn(message) {
93
+ console.warn(Logger.format("warn", message));
49
94
  }
50
- error(message) {
51
- console.error(this.format("error", message));
95
+ static error(message) {
96
+ console.error(Logger.format("error", message));
52
97
  }
53
- fail(message) {
54
- this.error(message);
55
- throw new Error(typeof message === "string" ? message : JSON.stringify(message, null, 2));
98
+ static fail(message) {
99
+ const formatted = Logger.format("error", Logger.stringify(message));
100
+ console.error(formatted);
101
+ throw new Error(formatted);
102
+ }
103
+ static stringify(message) {
104
+ if (message instanceof Error) return message.stack ?? message.message;
105
+ return typeof message === "string" ? message : JSON.stringify(message, null, 2);
56
106
  }
57
107
  };
58
- var logger_default = new Logger();
108
+ var logger_default = Logger;
59
109
 
60
110
  //#endregion
61
111
  //#region src/config/foundryvtt-manifest.ts
62
- function loadManifest(config) {
112
+ async function loadManifest(config) {
63
113
  if (context?.manifest) return context.manifest;
64
114
  const publicDir = config.publicDir || "public";
65
- const MANIFEST_LOCATIONS = [
115
+ const paths = [
66
116
  "system.json",
67
117
  "module.json",
68
118
  `${publicDir}/system.json`,
69
119
  `${publicDir}/module.json`
70
- ];
71
- const foundPath = MANIFEST_LOCATIONS.map((relPath) => path.resolve(process.cwd(), relPath)).find((absPath) => fs.pathExistsSync(absPath));
120
+ ].map((f) => path.resolve(process.cwd(), f));
121
+ const idx = (await Promise.all(paths.map((p) => fs_utils_default.fileExists(p)))).findIndex(Boolean);
122
+ const foundPath = idx !== -1 ? paths[idx] : void 0;
72
123
  if (!foundPath) logger_default.fail(`Could not find a manifest file (system.json or module.json) in project root or ${publicDir}/.`);
73
124
  try {
74
- const data = fs.readJsonSync(foundPath);
125
+ const data = await fs_utils_default.readJson(foundPath);
75
126
  if (!data.id || typeof data.id !== "string") logger_default.fail(`Manifest at ${foundPath} is missing required "id" field.`);
76
127
  const hasEsmodules = Array.isArray(data.esmodules) && data.esmodules.length > 0;
77
128
  const hasScripts = Array.isArray(data.scripts) && data.scripts.length > 0;
78
129
  if (hasEsmodules === hasScripts) logger_default.fail(`Manifest at ${foundPath} must define exactly one of "esmodules" or "scripts".`);
79
- const result = {
130
+ return {
80
131
  manifestType: foundPath.includes("module.json") ? "module" : "system",
81
132
  id: data.id,
82
133
  esmodules: Array.isArray(data.esmodules) ? data.esmodules : [],
@@ -85,7 +136,6 @@ function loadManifest(config) {
85
136
  languages: Array.isArray(data.languages) ? data.languages : [],
86
137
  packs: Array.isArray(data.packs) ? data.packs : []
87
138
  };
88
- return result;
89
139
  } catch (err) {
90
140
  logger_default.fail(`Failed to read manifest at ${foundPath}: ${err?.message || err}`);
91
141
  }
@@ -122,8 +172,7 @@ function createPartialViteConfig(config) {
122
172
  rollupOptions: { output: {
123
173
  entryFileNames: fileName,
124
174
  assetFileNames: (assetInfo) => {
125
- const names = assetInfo.names ?? [];
126
- if (names.some((n) => n.endsWith(".css"))) return cssFileName;
175
+ if ((assetInfo.names ?? []).some((n) => n.endsWith(".css"))) return cssFileName;
127
176
  return "[name][extname]";
128
177
  }
129
178
  } }
@@ -220,8 +269,7 @@ var PathUtils = class PathUtils {
220
269
  static getSourceDirectory() {
221
270
  if (!PathUtils._sourceDirectory) {
222
271
  const config = PathUtils.getConfig();
223
- const normalizedEntry = path.normalize(config.build.lib.entry.toString());
224
- const segments = normalizedEntry.split(path.sep).filter(Boolean).filter((s) => s !== ".");
272
+ const segments = path.normalize(config.build.lib.entry.toString()).split(path.sep).filter(Boolean).filter((s) => s !== ".");
225
273
  const firstFolder = segments.length > 0 ? segments[0] : ".";
226
274
  PathUtils._sourceDirectory = path.join(config.root, firstFolder);
227
275
  }
@@ -248,28 +296,28 @@ var PathUtils = class PathUtils {
248
296
  }
249
297
  return PathUtils._root;
250
298
  }
251
- static getOutDirFile(p) {
299
+ static async getOutDirFile(p) {
252
300
  const file = path.join(PathUtils.getOutDir(), p);
253
- return fs.existsSync(file) ? file : "";
301
+ return await fs_utils_default.fileExists(file) ? file : "";
254
302
  }
255
- static getPublicDirFile(p) {
303
+ static async getPublicDirFile(p) {
256
304
  const file = path.join(PathUtils.getPublicDir(), p);
257
- return fs.existsSync(file) ? file : "";
305
+ return await fs_utils_default.fileExists(file) ? file : "";
258
306
  }
259
- static findLocalFilePath(p) {
307
+ static async findLocalFilePath(p) {
260
308
  const fileCandidates = [
261
309
  PathUtils.getPublicDir(),
262
310
  PathUtils.getSourceDirectory(),
263
311
  PathUtils.getRoot()
264
312
  ].map((pth) => path.join(pth, p));
265
- return fileCandidates.find((pth) => fs.existsSync(pth)) ?? null;
313
+ const idx = (await Promise.all(fileCandidates.map(fs_utils_default.fileExists))).findIndex(Boolean);
314
+ return idx !== -1 ? fileCandidates[idx] : null;
266
315
  }
267
316
  static isFoundryVTTUrl(p) {
268
317
  const decodedBase = PathUtils.getDecodedBase();
269
- const pathToCheck = path.posix.normalize(p);
270
- return pathToCheck.startsWith(decodedBase);
318
+ return path.posix.normalize(p).startsWith(decodedBase);
271
319
  }
272
- static foundryVTTUrlToLocal(p) {
320
+ static async foundryVTTUrlToLocal(p) {
273
321
  const decodedBase = PathUtils.getDecodedBase();
274
322
  let pathToTransform = path.posix.normalize("/" + p);
275
323
  if (!pathToTransform.startsWith(decodedBase)) return null;
@@ -290,8 +338,7 @@ var PathUtils = class PathUtils {
290
338
  }
291
339
  static getLanguageSourcePath(p, lang) {
292
340
  const dir = path.parse(p).dir;
293
- const lastDirName = path.basename(dir);
294
- const finalSegments = lastDirName === lang ? dir : path.join(dir, lang);
341
+ const finalSegments = path.basename(dir) === lang ? dir : path.join(dir, lang);
295
342
  return path.join(PathUtils.getSourceDirectory(), finalSegments);
296
343
  }
297
344
  };
@@ -299,33 +346,33 @@ var path_utils_default = PathUtils;
299
346
 
300
347
  //#endregion
301
348
  //#region src/language/loader.ts
302
- function getLocalLanguageFiles(lang, outDir = false) {
303
- const manifest = context.manifest;
304
- const language = manifest.languages.find((l) => l.lang === lang);
349
+ async function getLocalLanguageFiles(lang, outDir = false) {
350
+ const language = context.manifest.languages.find((l) => l.lang === lang);
305
351
  if (!language) logger_default.fail(`Cannot find language "${lang}"`);
306
352
  const langPath = language?.path ?? "";
307
- if (outDir) {
308
- const languageFile = path_utils_default.getOutDirFile(langPath);
309
- return [languageFile];
310
- }
311
- const publicDirFile = path_utils_default.getPublicDirFile(langPath);
353
+ if (outDir) return [await path_utils_default.getOutDirFile(langPath)];
354
+ const publicDirFile = await path_utils_default.getPublicDirFile(langPath);
312
355
  if (publicDirFile !== "") return [publicDirFile];
313
356
  const sourcePath = path_utils_default.getLanguageSourcePath(langPath, lang);
314
- if (!fs.existsSync(sourcePath) || !fs.statSync(sourcePath).isDirectory()) {
315
- logger_default.warn(`No language folder found at: ${sourcePath}`);
316
- return [];
317
- }
318
- return globSync(posix.join(sourcePath, "**/*.json"));
357
+ if (await fs_utils_default.dirExists(sourcePath)) return await glob(path.join(sourcePath, "**/*.json"), { absolute: true });
358
+ logger_default.warn(`No language folder found at: ${sourcePath}`);
359
+ return [];
319
360
  }
320
- function loadLanguage(lang, outDir = false) {
321
- const files = getLocalLanguageFiles(lang, outDir);
361
+ async function loadLanguage(lang, outDir = false) {
362
+ const files = await getLocalLanguageFiles(lang, outDir);
322
363
  const result = /* @__PURE__ */ new Map();
323
- for (const file of files) try {
324
- result.set(file, fs.readJSONSync(file));
325
- languageTracker.addFile(lang, file);
326
- } catch (e) {
327
- logger_default.warn(e);
328
- }
364
+ const reads = files.map(async (file) => {
365
+ try {
366
+ const json = await fs_utils_default.readJson(file);
367
+ languageTracker.addFile(lang, file);
368
+ return [file, json];
369
+ } catch (e) {
370
+ logger_default.warn(e);
371
+ return null;
372
+ }
373
+ });
374
+ const results = await Promise.all(reads);
375
+ for (const entry of results) if (entry) result.set(entry[0], entry[1]);
329
376
  return result;
330
377
  }
331
378
 
@@ -364,9 +411,9 @@ function transform(dataMap) {
364
411
 
365
412
  //#endregion
366
413
  //#region src/language/validator.ts
367
- function validator() {
414
+ async function validator() {
368
415
  const manifest = context.manifest;
369
- const baseLanguageData = loadLanguage("en", true);
416
+ const baseLanguageData = await loadLanguage("en", true);
370
417
  if (baseLanguageData.size === 0) {
371
418
  logger_default.error("Base language \"en\" not found or could not be loaded.");
372
419
  return;
@@ -374,17 +421,17 @@ function validator() {
374
421
  const base = flattenKeys(baseLanguageData.values().next().value);
375
422
  for (const lang of manifest.languages) {
376
423
  if (lang.lang === "en") continue;
377
- const currentLanguageData = loadLanguage(lang.lang, true);
424
+ const currentLanguageData = await loadLanguage(lang.lang, true);
378
425
  if (currentLanguageData.size === 0) {
379
- console.warn(`Summary for language [${lang.lang}]: Could not be loaded.`);
426
+ logger_default.warn(`Summary for language [${lang.lang}]: Could not be loaded.`);
380
427
  continue;
381
428
  }
382
429
  const current = flattenKeys(currentLanguageData.values().next().value);
383
430
  const missing = Object.keys(base).filter((key) => !(key in current));
384
431
  const extra = Object.keys(current).filter((key) => !(key in base));
385
- console.log(`Summary for language [${lang.lang}]:`);
386
- if (missing.length) console.warn(`\tMissing keys: ${missing.length}`, missing.slice(0, 5));
387
- if (extra.length) console.warn(`\tExtra keys: ${extra.length}`, extra.slice(0, 5));
432
+ logger_default.info(`Summary for language [${lang.lang}]:`);
433
+ if (missing.length) console.warn(`Missing keys: ${missing.length}`, missing.slice(0, 5));
434
+ if (extra.length) console.warn(`Extra keys: ${extra.length}`, extra.slice(0, 5));
388
435
  if (!missing.length && !extra.length) console.log(" ✅ All keys match.");
389
436
  }
390
437
  }
@@ -396,16 +443,19 @@ async function compileManifestPacks() {
396
443
  for (const pack of context.manifest.packs) {
397
444
  const srcCandidates = [path.resolve(path_utils_default.getSourceDirectory(), pack.path), path.resolve(path_utils_default.getRoot(), pack.path)];
398
445
  const dest = path.resolve(path_utils_default.getOutDir(), pack.path);
399
- const chosenSrc = srcCandidates.find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isDirectory());
446
+ let chosenSrc;
447
+ for (const candidate of srcCandidates) if (await fs_utils_default.dirExists(candidate)) {
448
+ chosenSrc = candidate;
449
+ break;
450
+ }
400
451
  if (!chosenSrc) {
401
452
  logger_default.warn(`Pack path not found for ${pack.path}, skipped.`);
402
453
  continue;
403
454
  }
404
- const entries = fs.readdirSync(chosenSrc, {
405
- recursive: true,
406
- encoding: "utf8"
407
- });
408
- const hasYaml = entries.some((entry) => entry.endsWith(".yaml") || entry.endsWith(".yml"));
455
+ const hasYaml = (await glob(["**/*.yaml", "**/*.yml"], {
456
+ cwd: chosenSrc,
457
+ absolute: true
458
+ })).length > 0;
409
459
  await compilePack(chosenSrc, dest, {
410
460
  yaml: hasYaml,
411
461
  recursive: true
@@ -430,7 +480,7 @@ const handlebarsTracker = new HandlebarsTracker();
430
480
  //#endregion
431
481
  //#region src/server/http-middleware.ts
432
482
  function httpMiddlewareHook(server) {
433
- server.middlewares.use((req, res, next) => {
483
+ server.middlewares.use(async (req, res, next) => {
434
484
  const config = context.config;
435
485
  if (!path_utils_default.isFoundryVTTUrl(req.url ?? "")) {
436
486
  next();
@@ -447,7 +497,7 @@ function httpMiddlewareHook(server) {
447
497
  const languages = context.manifest.languages.filter((lang) => path_utils_default.localToFoundryVTTUrl(lang.path) === path.posix.normalize(req.url ?? ""));
448
498
  if (languages.length === 1) {
449
499
  const lang = languages[0].lang;
450
- const language = loadLanguage(lang);
500
+ const language = await loadLanguage(lang);
451
501
  const jsonData = transform(language);
452
502
  res.setHeader("Content-Type", "application/json");
453
503
  res.end(JSON.stringify(jsonData, null, 2));
@@ -461,20 +511,20 @@ function httpMiddlewareHook(server) {
461
511
  //#region src/server/socket-proxy.ts
462
512
  function socketProxy(server) {
463
513
  const env = context.env;
464
- const ioProxy = new Server(server.httpServer, { path: "/socket.io" });
465
- ioProxy.on("connection", (socket) => {
514
+ new Server(server.httpServer, { path: "/socket.io" }).on("connection", (socket) => {
466
515
  const upstream = io(`http://${env.foundryUrl}:${env.foundryPort}`, {
467
516
  transports: ["websocket"],
468
517
  upgrade: false,
469
518
  query: socket.handshake.query
470
519
  });
471
- socket.onAny((event, ...args) => {
520
+ socket.onAny(async (event, ...args) => {
472
521
  const maybeAck = typeof args[args.length - 1] === "function" ? args.pop() : null;
473
522
  if (event === "template") {
474
- const localPath = path_utils_default.foundryVTTUrlToLocal(args[0]);
523
+ const localPath = await path_utils_default.foundryVTTUrlToLocal(args[0]);
475
524
  if (localPath) {
525
+ const html = await fs_utils_default.readFile(localPath);
476
526
  if (maybeAck) maybeAck({
477
- html: fs.readFileSync(localPath, "utf8"),
527
+ html,
478
528
  success: true
479
529
  });
480
530
  handlebarsTracker.addFile(args[0], localPath);
@@ -486,8 +536,7 @@ function socketProxy(server) {
486
536
  });
487
537
  });
488
538
  upstream.onAny((event, ...args) => {
489
- const lastArg = args[args.length - 1];
490
- const maybeAck = typeof lastArg === "function" ? args.pop() : null;
539
+ const maybeAck = typeof args[args.length - 1] === "function" ? args.pop() : null;
491
540
  socket.emit(event, ...args, (response) => {
492
541
  if (maybeAck) maybeAck(response);
493
542
  });
@@ -585,24 +634,23 @@ if (import.meta.hot) {
585
634
 
586
635
  //#endregion
587
636
  //#region src/index.ts
588
- function foundryVTTPlugin(options = { buildPacks: true }) {
589
- context.env = loadEnv();
637
+ async function foundryVTTPlugin(options = { buildPacks: true }) {
638
+ context.env = await loadEnv();
590
639
  return {
591
640
  name: "vite-plugin-fvtt",
592
- config(config) {
593
- context.manifest = loadManifest(config) ?? void 0;
641
+ async config(config) {
642
+ context.manifest = await loadManifest(config) ?? void 0;
594
643
  return createPartialViteConfig(config);
595
644
  },
596
645
  configResolved(config) {
597
646
  context.config = config;
598
647
  },
599
648
  async generateBundle() {
600
- const manifestCandidates = ["system.json", "module.json"];
601
- for (const file of manifestCandidates) {
602
- const src = posix.resolve(file);
603
- if (!path_utils_default.getPublicDirFile(file) && fs.existsSync(src)) {
649
+ for (const file of ["system.json", "module.json"]) {
650
+ const src = path.resolve(file);
651
+ if (!path_utils_default.getPublicDirFile(file) && await fs_utils_default.fileExists(src)) {
604
652
  this.addWatchFile(src);
605
- const manifest = fs.readJsonSync(src);
653
+ const manifest = await fs_utils_default.readJson(src);
606
654
  this.emitFile({
607
655
  type: "asset",
608
656
  fileName: file,
@@ -612,13 +660,15 @@ function foundryVTTPlugin(options = { buildPacks: true }) {
612
660
  }
613
661
  const languages = context.manifest?.languages ?? [];
614
662
  if (languages.length > 0) for (const language of languages) {
615
- if (path_utils_default.getPublicDirFile(language.path)) continue;
616
- getLocalLanguageFiles(language.lang).forEach((langFile) => this.addWatchFile(langFile));
617
- const languageDataRaw = loadLanguage(language.lang);
663
+ if (await path_utils_default.getPublicDirFile(language.path)) continue;
664
+ getLocalLanguageFiles(language.lang).then((langFiles) => {
665
+ langFiles.forEach((file) => this.addWatchFile(file));
666
+ });
667
+ const languageDataRaw = await loadLanguage(language.lang);
618
668
  const languageData = transform(languageDataRaw);
619
669
  this.emitFile({
620
670
  type: "asset",
621
- fileName: posix.join(language.path),
671
+ fileName: path.join(language.path),
622
672
  source: JSON.stringify(languageData, null, 2)
623
673
  });
624
674
  }
@@ -627,17 +677,12 @@ function foundryVTTPlugin(options = { buildPacks: true }) {
627
677
  if (options.buildPacks) await compileManifestPacks();
628
678
  },
629
679
  closeBundle() {
630
- const languages = context.manifest?.languages ?? [];
631
- if (languages.length > 0) validator();
680
+ if ((context.manifest?.languages ?? []).length > 0) validator();
632
681
  },
633
682
  load(id) {
634
683
  const config = context.config;
635
684
  const jsFileName = (config.build.rollupOptions?.output).entryFileNames;
636
- if (id === jsFileName || id === `/${jsFileName}`) {
637
- const entryPath = posix.resolve(config.build.lib.entry);
638
- const viteId = `/@fs/${entryPath}`;
639
- return `import '${viteId}';\n${hmr_client_default}`;
640
- }
685
+ if (id === jsFileName || id === `/${jsFileName}`) return `import '${`/@fs/${path.resolve(config.build.lib.entry)}`}';\n${hmr_client_default}`;
641
686
  },
642
687
  configureServer: setupDevServer
643
688
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-fvtt",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "A Vite plugin for module and system development for Foundry VTT",
5
5
  "keywords": [
6
6
  "vite",
@@ -28,23 +28,22 @@
28
28
  ],
29
29
  "scripts": {
30
30
  "dev": "tsdown --watch",
31
- "build": "tsdown"
31
+ "build": "tsdown",
32
+ "test": "vitest"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@eslint/js": "^9.34.0",
35
- "@types/fs-extra": "^11.0.4",
36
36
  "@types/node": "^24.3.0",
37
37
  "eslint": "^9.34.0",
38
38
  "prettier": "^3.6.2",
39
- "tsdown": "^0.14.2",
39
+ "tsdown": "^0.15.1",
40
40
  "typescript": "^5.9.2",
41
41
  "typescript-eslint": "^8.41.0",
42
- "vite": "*"
42
+ "vite": "*",
43
+ "vitest": "^3.2.4"
43
44
  },
44
45
  "dependencies": {
45
46
  "@foundryvtt/foundryvtt-cli": "^3.0.0",
46
- "dotenv": "^17.2.1",
47
- "fs-extra": "^11.3.1",
48
47
  "socket.io": "^4.8.1",
49
48
  "socket.io-client": "^4.8.1",
50
49
  "tinyglobby": "^0.2.15"