vite-plugin-fvtt 0.2.5 → 0.2.7

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/dist/index.js DELETED
@@ -1,692 +0,0 @@
1
- import path from "path";
2
- import { glob } from "tinyglobby";
3
- import fs from "fs/promises";
4
- import { compilePack } from "@foundryvtt/foundryvtt-cli";
5
- import { Server } from "socket.io";
6
- import { io } from "socket.io-client";
7
-
8
- //#region src/context.ts
9
- const context = {};
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
-
42
- //#endregion
43
- //#region src/config/env.ts
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
- }
67
- return {
68
- foundryUrl: merged.FOUNDRY_URL,
69
- foundryPort: parseInt(merged.FOUNDRY_PORT, 10)
70
- };
71
- }
72
-
73
- //#endregion
74
- //#region src/utils/logger.ts
75
- var Logger = class Logger {
76
- static namespace = "vite-plugin-fvtt";
77
- static colors = {
78
- info: "\x1B[36m",
79
- warn: "\x1B[33m",
80
- error: "\x1B[31m"
81
- };
82
- static reset = "\x1B[0m";
83
- initialize(namespace = "vite-plugin-fvtt") {
84
- Logger.namespace = namespace;
85
- }
86
- static format(level, message) {
87
- return `${Logger.colors[level] ?? ""}[${Logger.namespace}] [${level.toUpperCase()}]${Logger.reset} ${message}`;
88
- }
89
- static info(message) {
90
- console.log(Logger.format("info", message));
91
- }
92
- static warn(message) {
93
- console.warn(Logger.format("warn", message));
94
- }
95
- static error(message) {
96
- console.error(Logger.format("error", message));
97
- }
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);
106
- }
107
- };
108
- var logger_default = Logger;
109
-
110
- //#endregion
111
- //#region src/config/foundryvtt-manifest.ts
112
- async function loadManifest(config) {
113
- if (context?.manifest) return context.manifest;
114
- const publicDir = config.publicDir || "public";
115
- const paths = [
116
- "system.json",
117
- "module.json",
118
- `${publicDir}/system.json`,
119
- `${publicDir}/module.json`
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;
123
- if (!foundPath) logger_default.fail(`Could not find a manifest file (system.json or module.json) in project root or ${publicDir}/.`);
124
- try {
125
- const data = await fs_utils_default.readJson(foundPath);
126
- if (!data.id || typeof data.id !== "string") logger_default.fail(`Manifest at ${foundPath} is missing required "id" field.`);
127
- const hasEsmodules = Array.isArray(data.esmodules) && data.esmodules.length > 0;
128
- const hasScripts = Array.isArray(data.scripts) && data.scripts.length > 0;
129
- if (hasEsmodules === hasScripts) logger_default.fail(`Manifest at ${foundPath} must define exactly one of "esmodules" or "scripts".`);
130
- return {
131
- manifestType: foundPath.includes("module.json") ? "module" : "system",
132
- id: data.id,
133
- esmodules: Array.isArray(data.esmodules) ? data.esmodules : [],
134
- scripts: Array.isArray(data.scripts) ? data.scripts : [],
135
- styles: Array.isArray(data.styles) ? data.styles : [],
136
- languages: Array.isArray(data.languages) ? data.languages : [],
137
- packs: Array.isArray(data.packs) ? data.packs : []
138
- };
139
- } catch (err) {
140
- logger_default.fail(`Failed to read manifest at ${foundPath}: ${err?.message || err}`);
141
- }
142
- }
143
-
144
- //#endregion
145
- //#region src/config/vite-options.ts
146
- function createPartialViteConfig(config) {
147
- const base = config.base ?? `/${context.manifest?.manifestType}s/${context.manifest?.id}/`;
148
- const useEsModules = context.manifest?.esmodules.length === 1;
149
- const formats = useEsModules ? ["es"] : ["umd"];
150
- const fileName = (useEsModules ? context.manifest?.esmodules[0] : context.manifest?.scripts?.[0]) ?? "scripts/bundle.js";
151
- if (!(useEsModules || context.manifest?.scripts?.[0])) logger_default.warn("No output file specified in manifest, using default \"bundle\" in the \"scripts/\" folder");
152
- if (!context.manifest?.styles?.length) logger_default.warn("No CSS file found in manifest");
153
- const cssFileName = context.manifest?.styles[0] ?? "styles/bundle.css";
154
- if (!context.manifest?.styles[0]) logger_default.warn("No output css file specified in manifest, using default \"bundle\" in the \"styles/\" folder");
155
- const foundryPort = context.env?.foundryPort ?? 3e4;
156
- const foundryUrl = context.env?.foundryUrl ?? "localhost";
157
- const entry = (config.build?.lib)?.entry;
158
- if (!entry) logger_default.fail("Entry must be specified in lib");
159
- if (typeof entry !== "string") logger_default.fail("Only a singular string entry is supported for build.lib.entry");
160
- const isWatch = process.argv.includes("--watch") || !!config.build?.watch;
161
- return {
162
- base,
163
- build: {
164
- emptyOutDir: config.build?.emptyOutDir ?? !isWatch,
165
- lib: {
166
- entry,
167
- formats,
168
- name: context.manifest?.id ?? "bundle",
169
- cssFileName: "bundle"
170
- },
171
- minify: "esbuild",
172
- rollupOptions: { output: {
173
- entryFileNames: fileName,
174
- assetFileNames: (assetInfo) => {
175
- if ((assetInfo.names ?? []).some((n) => n.endsWith(".css"))) return cssFileName;
176
- return "[name][extname]";
177
- }
178
- } }
179
- },
180
- define: { __FVTT_PLUGIN__: {
181
- id: context.manifest?.id,
182
- isSystem: context.manifest?.manifestType === "system"
183
- } },
184
- esbuild: config.esbuild ?? {
185
- minifyIdentifiers: false,
186
- minifySyntax: true,
187
- minifyWhitespace: true,
188
- keepNames: true
189
- },
190
- server: {
191
- port: foundryPort + 1,
192
- proxy: { [`^(?!${base})`]: `http://${foundryUrl}:${foundryPort}` }
193
- }
194
- };
195
- }
196
-
197
- //#endregion
198
- //#region src/server/trackers/abstract-file-tracker.ts
199
- var AbstractFileTracker = class {
200
- initialized = false;
201
- tracked = /* @__PURE__ */ new Map();
202
- watcher = null;
203
- config;
204
- constructor(config) {
205
- this.config = config;
206
- }
207
- initialize(server) {
208
- if (this.initialized) return;
209
- this.initialized = true;
210
- this.watcher = server.watcher;
211
- this.watcher.on("change", (changedPath) => {
212
- const value = this.tracked.get(changedPath);
213
- if (!value) return;
214
- logger_default.info(`Attempting to hot reload ${changedPath}`);
215
- const eventData = this.getEventData(changedPath, value);
216
- server.ws.send({
217
- type: "custom",
218
- event: this.updateEvent,
219
- data: eventData
220
- });
221
- });
222
- }
223
- addFile(value, filePath) {
224
- const absPath = path.resolve(filePath);
225
- if (!this.tracked.has(absPath)) {
226
- this.tracked.set(absPath, value);
227
- this.watcher?.add(absPath);
228
- }
229
- }
230
- };
231
-
232
- //#endregion
233
- //#region src/server/trackers/language-tracker.ts
234
- var LanguageTracker = class extends AbstractFileTracker {
235
- updateEvent = "foundryvtt-language-update";
236
- constructor() {
237
- super({});
238
- }
239
- getEventData() {
240
- return {};
241
- }
242
- };
243
- const languageTracker = new LanguageTracker();
244
-
245
- //#endregion
246
- //#region src/utils/path-utils.ts
247
- var PathUtils = class PathUtils {
248
- static _config = null;
249
- static _sourceDirectory = null;
250
- static _decodedBase = null;
251
- static _publicDir = null;
252
- static _outDir = null;
253
- static _root = null;
254
- static getConfig() {
255
- if (!PathUtils._config) {
256
- const config = context.config;
257
- if (!config) logger_default.fail("Path utils can only be called after vite has resolved the config");
258
- PathUtils._config = config;
259
- }
260
- return PathUtils._config;
261
- }
262
- static getDecodedBase() {
263
- if (!PathUtils._decodedBase) {
264
- const config = PathUtils.getConfig();
265
- PathUtils._decodedBase = path.posix.normalize(decodeURI(config.base));
266
- }
267
- return PathUtils._decodedBase;
268
- }
269
- static getSourceDirectory() {
270
- if (!PathUtils._sourceDirectory) {
271
- const config = PathUtils.getConfig();
272
- const segments = path.normalize(config.build.lib.entry.toString()).split(path.sep).filter(Boolean).filter((s) => s !== ".");
273
- const firstFolder = segments.length > 0 ? segments[0] : ".";
274
- PathUtils._sourceDirectory = path.join(config.root, firstFolder);
275
- }
276
- return PathUtils._sourceDirectory;
277
- }
278
- static getPublicDir() {
279
- if (!PathUtils._publicDir) {
280
- const config = PathUtils.getConfig();
281
- PathUtils._publicDir = path.resolve(config.publicDir);
282
- }
283
- return PathUtils._publicDir;
284
- }
285
- static getOutDir() {
286
- if (!PathUtils._outDir) {
287
- const config = PathUtils.getConfig();
288
- PathUtils._outDir = path.resolve(config.build.outDir);
289
- }
290
- return PathUtils._outDir;
291
- }
292
- static getRoot() {
293
- if (!PathUtils._root) {
294
- const config = PathUtils.getConfig();
295
- PathUtils._root = path.resolve(config.root);
296
- }
297
- return PathUtils._root;
298
- }
299
- static async getOutDirFile(p) {
300
- const file = path.join(PathUtils.getOutDir(), p);
301
- return await fs_utils_default.fileExists(file) ? file : "";
302
- }
303
- static async getPublicDirFile(p) {
304
- const file = path.join(PathUtils.getPublicDir(), p);
305
- return await fs_utils_default.fileExists(file) ? file : "";
306
- }
307
- static async findLocalFilePath(p) {
308
- const fileCandidates = [
309
- PathUtils.getPublicDir(),
310
- PathUtils.getSourceDirectory(),
311
- PathUtils.getRoot()
312
- ].map((pth) => path.join(pth, p));
313
- const idx = (await Promise.all(fileCandidates.map(fs_utils_default.fileExists))).findIndex(Boolean);
314
- return idx !== -1 ? fileCandidates[idx] : null;
315
- }
316
- static isFoundryVTTUrl(p) {
317
- const decodedBase = PathUtils.getDecodedBase();
318
- return path.posix.normalize(p).startsWith(decodedBase);
319
- }
320
- static async foundryVTTUrlToLocal(p) {
321
- const decodedBase = PathUtils.getDecodedBase();
322
- let pathToTransform = path.posix.normalize("/" + p);
323
- if (!pathToTransform.startsWith(decodedBase)) return null;
324
- pathToTransform = path.relative(decodedBase, pathToTransform);
325
- return PathUtils.findLocalFilePath(pathToTransform);
326
- }
327
- static localToFoundryVTTUrl(p) {
328
- const decodedBase = PathUtils.getDecodedBase();
329
- let pathToTransform = path.normalize(p);
330
- [
331
- PathUtils.getPublicDir(),
332
- PathUtils.getSourceDirectory(),
333
- PathUtils.getRoot()
334
- ].forEach((pth) => {
335
- if (pathToTransform.startsWith(pth)) pathToTransform = pathToTransform.slice(pth.length);
336
- });
337
- return path.join(decodedBase, pathToTransform);
338
- }
339
- static getLanguageSourcePath(p, lang) {
340
- const dir = path.parse(p).dir;
341
- const finalSegments = path.basename(dir) === lang ? dir : path.join(dir, lang);
342
- return path.join(PathUtils.getSourceDirectory(), finalSegments);
343
- }
344
- };
345
- var path_utils_default = PathUtils;
346
-
347
- //#endregion
348
- //#region src/language/loader.ts
349
- async function getLocalLanguageFiles(lang, outDir = false) {
350
- const language = context.manifest.languages.find((l) => l.lang === lang);
351
- if (!language) logger_default.fail(`Cannot find language "${lang}"`);
352
- const langPath = language?.path ?? "";
353
- if (outDir) return [await path_utils_default.getOutDirFile(langPath)];
354
- const publicDirFile = await path_utils_default.getPublicDirFile(langPath);
355
- if (publicDirFile !== "") return [publicDirFile];
356
- const sourcePath = path_utils_default.getLanguageSourcePath(langPath, lang);
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 [];
360
- }
361
- async function loadLanguage(lang, outDir = false) {
362
- const files = await getLocalLanguageFiles(lang, outDir);
363
- const result = /* @__PURE__ */ new Map();
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]);
376
- return result;
377
- }
378
-
379
- //#endregion
380
- //#region src/language/transformer.ts
381
- function flattenKeys(obj, prefix = "") {
382
- const result = {};
383
- for (const [key, val] of Object.entries(obj)) {
384
- const fullKey = prefix ? `${prefix}.${key}` : key;
385
- if (val && typeof val === "object" && !Array.isArray(val)) Object.assign(result, flattenKeys(val, fullKey));
386
- else result[fullKey] = val;
387
- }
388
- return result;
389
- }
390
- function expandDotNotationKeys(target, source, depth = 0) {
391
- if (depth > 32) logger_default.fail("Max object expansion depth exceeded.");
392
- if (!source || typeof source !== "object" || Array.isArray(source)) return source;
393
- for (const [key, value] of Object.entries(source)) {
394
- let current = target;
395
- const parts = key.split(".");
396
- const lastKey = parts.pop();
397
- for (const part of parts) {
398
- if (!(part in current)) current[part] = {};
399
- current = current[part];
400
- }
401
- if (lastKey in current) console.warn(`Warning: Overwriting key "${lastKey}" during transformation.`);
402
- current[lastKey] = expandDotNotationKeys({}, value, depth + 1);
403
- }
404
- return target;
405
- }
406
- function transform(dataMap) {
407
- const mergedData = {};
408
- for (const data of dataMap.values()) expandDotNotationKeys(mergedData, data);
409
- return mergedData;
410
- }
411
-
412
- //#endregion
413
- //#region src/language/validator.ts
414
- async function validator() {
415
- const manifest = context.manifest;
416
- const baseLanguageData = await loadLanguage("en", true);
417
- if (baseLanguageData.size === 0) {
418
- logger_default.error("Base language \"en\" not found or could not be loaded.");
419
- return;
420
- }
421
- const base = flattenKeys(baseLanguageData.values().next().value);
422
- for (const lang of manifest.languages) {
423
- if (lang.lang === "en") continue;
424
- const currentLanguageData = await loadLanguage(lang.lang, true);
425
- if (currentLanguageData.size === 0) {
426
- logger_default.warn(`Summary for language [${lang.lang}]: Could not be loaded.`);
427
- continue;
428
- }
429
- const current = flattenKeys(currentLanguageData.values().next().value);
430
- const missing = Object.keys(base).filter((key) => !(key in current));
431
- const extra = Object.keys(current).filter((key) => !(key in base));
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));
435
- if (!missing.length && !extra.length) console.log(" ✅ All keys match.");
436
- }
437
- }
438
-
439
- //#endregion
440
- //#region src/packs/compile-packs.ts
441
- async function compileManifestPacks() {
442
- if (!context.manifest?.packs) return;
443
- for (const pack of context.manifest.packs) {
444
- const srcCandidates = [path.resolve(path_utils_default.getSourceDirectory(), pack.path), path.resolve(path_utils_default.getRoot(), pack.path)];
445
- const dest = path.resolve(path_utils_default.getOutDir(), pack.path);
446
- let chosenSrc;
447
- for (const candidate of srcCandidates) if (await fs_utils_default.dirExists(candidate)) {
448
- chosenSrc = candidate;
449
- break;
450
- }
451
- if (!chosenSrc) {
452
- logger_default.warn(`Pack path not found for ${pack.path}, skipped.`);
453
- continue;
454
- }
455
- const hasYaml = (await glob(["**/*.yaml", "**/*.yml"], {
456
- cwd: chosenSrc,
457
- absolute: true
458
- })).length > 0;
459
- await compilePack(chosenSrc, dest, {
460
- yaml: hasYaml,
461
- recursive: true
462
- });
463
- logger_default.info(`Compiled pack ${pack.path} (${hasYaml ? "YAML" : "JSON"}) from ${chosenSrc}`);
464
- }
465
- }
466
-
467
- //#endregion
468
- //#region src/server/trackers/handlebars-tracker.ts
469
- var HandlebarsTracker = class extends AbstractFileTracker {
470
- updateEvent = "foundryvtt-template-update";
471
- constructor() {
472
- super(context.config);
473
- }
474
- getEventData(changedPath, value) {
475
- return { path: value };
476
- }
477
- };
478
- const handlebarsTracker = new HandlebarsTracker();
479
-
480
- //#endregion
481
- //#region src/server/http-middleware.ts
482
- function httpMiddlewareHook(server) {
483
- server.middlewares.use(async (req, res, next) => {
484
- const config = context.config;
485
- if (!path_utils_default.isFoundryVTTUrl(req.url ?? "")) {
486
- next();
487
- return;
488
- }
489
- const cssFileName = config.build.lib.cssFileName;
490
- const cssEntry = cssFileName ? path_utils_default.localToFoundryVTTUrl(`${cssFileName}.css`) : null;
491
- if (path.posix.normalize(req.url ?? "") === cssEntry) {
492
- logger_default.info(`Blocking CSS entry to ${req.url}`);
493
- res.setHeader("Content-Type", "text/css");
494
- res.end("/* The cake is in another castle. */");
495
- return;
496
- }
497
- const languages = context.manifest.languages.filter((lang) => path_utils_default.localToFoundryVTTUrl(lang.path) === path.posix.normalize(req.url ?? ""));
498
- if (languages.length === 1) {
499
- const lang = languages[0].lang;
500
- const language = await loadLanguage(lang);
501
- const jsonData = transform(language);
502
- res.setHeader("Content-Type", "application/json");
503
- res.end(JSON.stringify(jsonData, null, 2));
504
- return;
505
- }
506
- next();
507
- });
508
- }
509
-
510
- //#endregion
511
- //#region src/server/socket-proxy.ts
512
- function socketProxy(server) {
513
- const env = context.env;
514
- new Server(server.httpServer, { path: "/socket.io" }).on("connection", (socket) => {
515
- const upstream = io(`http://${env.foundryUrl}:${env.foundryPort}`, {
516
- transports: ["websocket"],
517
- upgrade: false,
518
- query: socket.handshake.query
519
- });
520
- socket.onAny(async (event, ...args) => {
521
- const maybeAck = typeof args[args.length - 1] === "function" ? args.pop() : null;
522
- if (event === "template") {
523
- const localPath = await path_utils_default.foundryVTTUrlToLocal(args[0]);
524
- if (localPath) {
525
- const html = await fs_utils_default.readFile(localPath);
526
- if (maybeAck) maybeAck({
527
- html,
528
- success: true
529
- });
530
- handlebarsTracker.addFile(args[0], localPath);
531
- return;
532
- }
533
- }
534
- upstream.emit(event, ...args, (response) => {
535
- if (maybeAck) maybeAck(response);
536
- });
537
- });
538
- upstream.onAny((event, ...args) => {
539
- const maybeAck = typeof args[args.length - 1] === "function" ? args.pop() : null;
540
- socket.emit(event, ...args, (response) => {
541
- if (maybeAck) maybeAck(response);
542
- });
543
- });
544
- });
545
- }
546
-
547
- //#endregion
548
- //#region src/server/index.ts
549
- function setupDevServer(server) {
550
- handlebarsTracker.initialize(server);
551
- languageTracker.initialize(server);
552
- httpMiddlewareHook(server);
553
- socketProxy(server);
554
- }
555
-
556
- //#endregion
557
- //#region src/server/hmr-client.ts
558
- var hmr_client_default = `
559
- if (import.meta.hot) {
560
- const FVTT_PLUGIN = __FVTT_PLUGIN__
561
-
562
- function refreshApplications(path = null) {
563
- // AppV1 refresh
564
- Object.values(foundry.ui.windows).forEach(app => app.render(true))
565
- // AppV2 refresh
566
- if (path)
567
- foundry.applications.instances.forEach(appV2 => {
568
- Object.values(appV2.constructor.PARTS ?? {}).forEach(part => {
569
- const templates = Array.isArray(part.templates) ? part.templates : []
570
- if (part.template === path || templates.includes(path)) appV2.render(true)
571
- })
572
- })
573
- else foundry.applications.instances.forEach(appV2 => appV2.render(true))
574
- }
575
-
576
- import.meta.hot.on('foundryvtt-template-update', ({ path }) => {
577
- game.socket.emit('template', path, response => {
578
- if (response.error) new Error(response.error)
579
- let template = undefined
580
- try {
581
- template = Handlebars.compile(response.html)
582
- } catch (error) {
583
- console.error(error)
584
- return
585
- }
586
- Handlebars.registerPartial(path, template)
587
- console.log(\`Vite | Retrieved and compiled template \${path}\`)
588
- refreshApplications(path)
589
- })
590
- })
591
-
592
- async function hmrLanguage(lang, targetObject = game.i18n.translations) {
593
- try {
594
- const languages = FVTT_PLUGIN.isSystem
595
- ? game.system.languages
596
- : game.modules.get(FVTT_PLUGIN.id)?.languages
597
- if (!languages) {
598
- console.warn(
599
- 'Vite | Got a HMR request to reload languages, however no languages were found.',
600
- )
601
- return
602
- }
603
- const langEntry = languages.find(l => l.lang === lang)
604
- if (!langEntry) {
605
- console.warn('Vite | Got an HMR request for an undefined language')
606
- return
607
- }
608
-
609
- const url = langEntry.path
610
- const resp = await fetch(url)
611
- if (!resp.ok) throw new Error('Failed to fetch language file!')
612
-
613
- const json = await resp.json()
614
-
615
- foundry.utils.mergeObject(targetObject, json)
616
- console.log(\`Vite | HMR: Reloaded language '\${lang}'\`)
617
- } catch (error) {
618
- console.error(\`Vite | HMR: Error reloading language '\${lang}' for \${FVTT_PLUGIN.id}\`, error);
619
- }
620
- }
621
-
622
- import.meta.hot.on('foundryvtt-language-update', async () => {
623
- const currentLang = game.i18n.lang
624
- const promises = []
625
- if (currentLang !== 'en') {
626
- promises.push(hmrLanguage('en', game.i18n._fallback))
627
- }
628
- promises.push(hmrLanguage(currentLang))
629
- await Promise.all(promises)
630
- refreshApplications()
631
- })
632
- } else console.error('Vite | HMR is disabled')
633
- //`;
634
-
635
- //#endregion
636
- //#region src/index.ts
637
- async function foundryVTTPlugin(options = { buildPacks: true }) {
638
- context.env = await loadEnv();
639
- return {
640
- name: "vite-plugin-fvtt",
641
- async config(config) {
642
- context.manifest = await loadManifest(config) ?? void 0;
643
- return createPartialViteConfig(config);
644
- },
645
- configResolved(config) {
646
- context.config = config;
647
- },
648
- async generateBundle() {
649
- for (const file of ["system.json", "module.json"]) {
650
- const src = path.resolve(file);
651
- if (!await path_utils_default.getPublicDirFile(file) && await fs_utils_default.fileExists(src)) {
652
- this.addWatchFile(src);
653
- const manifest = await fs_utils_default.readJson(src);
654
- this.emitFile({
655
- type: "asset",
656
- fileName: file,
657
- source: JSON.stringify(manifest, null, 2)
658
- });
659
- }
660
- }
661
- const languages = context.manifest?.languages ?? [];
662
- if (languages.length > 0) for (const language of languages) {
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);
668
- const languageData = transform(languageDataRaw);
669
- this.emitFile({
670
- type: "asset",
671
- fileName: path.join(language.path),
672
- source: JSON.stringify(languageData, null, 2)
673
- });
674
- }
675
- },
676
- async writeBundle() {
677
- if (options.buildPacks) await compileManifestPacks();
678
- },
679
- closeBundle() {
680
- if ((context.manifest?.languages ?? []).length > 0) validator();
681
- },
682
- load(id) {
683
- const config = context.config;
684
- const jsFileName = (config.build.rollupOptions?.output).entryFileNames;
685
- if (id === jsFileName || id === `/${jsFileName}`) return `import '${`/@fs/${path.resolve(config.build.lib.entry)}`}';\n${hmr_client_default}`;
686
- },
687
- configureServer: setupDevServer
688
- };
689
- }
690
-
691
- //#endregion
692
- export { foundryVTTPlugin as default };