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 +24 -3
- package/README.md +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +162 -117
- package/package.json +6 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [0.2.
|
|
3
|
+
## [0.2.4] - 2025-09-23
|
|
4
4
|
|
|
5
|
-
|
|
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.
|
|
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
|
|
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
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 {
|
|
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
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
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:
|
|
23
|
-
foundryPort: parseInt(
|
|
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[
|
|
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
|
-
|
|
37
|
-
|
|
82
|
+
static reset = "\x1B[0m";
|
|
83
|
+
initialize(namespace = "vite-plugin-fvtt") {
|
|
84
|
+
Logger.namespace = namespace;
|
|
38
85
|
}
|
|
39
|
-
format(level, message) {
|
|
40
|
-
|
|
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(
|
|
89
|
+
static info(message) {
|
|
90
|
+
console.log(Logger.format("info", message));
|
|
46
91
|
}
|
|
47
|
-
warn(message) {
|
|
48
|
-
console.warn(
|
|
92
|
+
static warn(message) {
|
|
93
|
+
console.warn(Logger.format("warn", message));
|
|
49
94
|
}
|
|
50
|
-
error(message) {
|
|
51
|
-
console.error(
|
|
95
|
+
static error(message) {
|
|
96
|
+
console.error(Logger.format("error", message));
|
|
52
97
|
}
|
|
53
|
-
fail(message) {
|
|
54
|
-
|
|
55
|
-
|
|
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 =
|
|
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
|
|
115
|
+
const paths = [
|
|
66
116
|
"system.json",
|
|
67
117
|
"module.json",
|
|
68
118
|
`${publicDir}/system.json`,
|
|
69
119
|
`${publicDir}/module.json`
|
|
70
|
-
];
|
|
71
|
-
const
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 (
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
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
|
-
|
|
386
|
-
if (missing.length) console.warn(
|
|
387
|
-
if (extra.length) console.warn(
|
|
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
|
-
|
|
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
|
|
405
|
-
|
|
406
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
601
|
-
|
|
602
|
-
|
|
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 =
|
|
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).
|
|
617
|
-
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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"
|