vite-plugin-fvtt 0.1.3 → 0.2.0

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 ADDED
@@ -0,0 +1,70 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ### Added
6
+
7
+ - Automatic compiling of packs for watch and build mode, when discovered.
8
+ - Option to skip the automatic compilation of packs `{ buildPacks: false }`.
9
+ - Documenting the changes in a `CHANGELOG.md` for the plugin.
10
+
11
+ ## [0.1.4] - 2025-09-06
12
+
13
+ ### Added
14
+
15
+ - In watch mode, the plugin now automatically avoids cleaning the output directory, if `emptyOutDir`
16
+ is not set.
17
+
18
+ ### Fixed
19
+
20
+ - By using rollupOptions, the output files are now correctly named as specified by the foundry vtt
21
+ manifest, even in case `{ "type": "module" }` is not set in the `package.json`.
22
+
23
+ ### Changed
24
+
25
+ - Defaults in case of unspecified `esmodules`, `scripts`, or `styles` in the foundry vtt manifest
26
+ now default to the folder structure suggested by foundry vtt.
27
+
28
+ ## [0.1.3] - 2025-09-04
29
+
30
+ ### Added
31
+
32
+ - Manifest and language files not in the public directory are now watched during `--watch`.
33
+
34
+ ### Changed
35
+
36
+ - Updated the documentation.
37
+
38
+ ### Fixed
39
+
40
+ - HMR for language files only manually reloads all active translations for the current module.
41
+ - HMR for templates now manually reassigns them on successful compilation instead of using
42
+ `getTemplate`. This prevents an missing template from appearing after a failed HMR compilation.
43
+
44
+ ## [0.1.2] - 2025-09-03
45
+
46
+ ### Added
47
+
48
+ - Automatic npm deployment on tag push.
49
+
50
+ ### Changed
51
+
52
+ - Replaced glob with tinyglobby to match vite's dependencies and not add more unnecessary modules.
53
+ - Default to `bundle.css` if no style file is specified in the manifest, but one was found during
54
+ compilation as an asset.
55
+
56
+ ### Fixed
57
+
58
+ - Add typechecks to guard against missing manifest entries.
59
+
60
+ ## [0.1.1] - 2025-09-02
61
+
62
+ ### Added
63
+
64
+ - Initial Release
65
+
66
+ [unreleased]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.1.4...HEAD
67
+ [0.1.4]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.1.3...v0.1.4
68
+ [0.1.3]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.1.2...v0.1.3
69
+ [0.1.2]: https://github.com/MatyeusM/vite-plugin-fvtt/compare/v0.1.1...v0.1.2
70
+ [0.1.1]: https://github.com/MatyeusM/vite-plugin-fvtt/releases/tag/v0.1.1
package/README.md CHANGED
@@ -1,83 +1,111 @@
1
1
  # **vite-plugin-fvtt**
2
2
 
3
- A [Vite](https://vitejs.dev/) plugin to **streamline and automate** the development of Foundry VTT modules and systems.
3
+ A [Vite](https://vitejs.dev/) plugin to **streamline and automate** the development of Foundry VTT
4
+ modules and systems.
4
5
 
5
- It handles manifest resolution, asset copying, language file composition, and template handling with **minimal setup**, letting you focus on your code.
6
+ It handles manifest resolution, asset copying, language file composition, and template handling with
7
+ **minimal setup**, letting you focus on your code.
6
8
 
7
- The plugin's core goal is to enable a robust HMR workflow via Vite's development server, freeing you from Foundry VTT's native HMR and build watch commands.
9
+ The plugin's core goal is to enable a robust HMR workflow via Vite's development server, freeing you
10
+ from Foundry VTT's native HMR and build watch commands.
11
+
12
+ [**Changelog**](CHANGELOG.md)
8
13
 
9
14
  ## **🚀 Getting Started**
10
15
 
11
16
  ### **Step 1. Setup a Foundry VTT Project**
12
17
 
13
- Create a standard [Foundry VTT module or system](https://foundryvtt.com/article/module-development/).
14
- Place your `module.json` or `system.json` manifest in either your **project root** or your **public/** directory.
18
+ Create a standard
19
+ [Foundry VTT module or system](https://foundryvtt.com/article/module-development/). Place your
20
+ `module.json` or `system.json` manifest in either your **project root** or your **public/**
21
+ directory.
15
22
 
16
23
  ### **Step 2. Add the Plugin to your Vite Config**
17
24
 
18
25
  Install the plugin with `npm i -D vite-plugin-fvtt`.
19
26
 
20
- Add the plugin to your vite.config.js. The **build.lib.entry** field is required; most of the other settings are inferred by the plugin from your Foundry VTT manifest.
27
+ Add the plugin to your vite.config.js. The **build.lib.entry** field is required; most of the other
28
+ settings are inferred by the plugin from your Foundry VTT manifest.
21
29
 
22
30
  ```js
23
31
  // vite.config.js
24
- import { defineConfig } from 'vite';
25
- import foundryVTT from 'vite-plugin-fvtt';
32
+ import { defineConfig } from 'vite'
33
+ import foundryVTT from 'vite-plugin-fvtt'
26
34
 
27
35
  export default defineConfig({
28
36
  plugins: [foundryVTT()],
29
37
  build: {
30
38
  // ⚠️ Required: The entry point for your module/system.
31
39
  // This file should import your main CSS/SCSS/LESS file.
32
- lib: {
33
- entry: './src/main.js',
34
- },
40
+ lib: { entry: './src/main.js' },
35
41
  sourcemap: true,
36
42
  },
37
- });
43
+ })
38
44
  ```
39
45
 
40
46
  ## **⚙️ Features**
41
47
 
42
48
  ### **1. Configuration**
43
- The plugin needs to know where your Foundry VTT instance is running to proxy and serve assets correctly. If you want to change anything from the defaults `http://localhost:30000`, create a `.env.foundryvtt.local` file in your project.
49
+
50
+ The plugin needs to know where your Foundry VTT instance is running to proxy and serve assets
51
+ correctly. If you want to change anything from the defaults `http://localhost:30000`, create a
52
+ `.env.foundryvtt.local` file in your project.
53
+
44
54
  ```ini
45
55
  FOUNDRY_URL=localhost
46
56
  FOUNDRY_PORT=30000
47
57
  ```
48
58
 
49
- The Vite dev server will run on `FOUNDRY_PORT + 1`, where you will need to open your browser manually to.
59
+ The Vite dev server will run on `FOUNDRY_PORT + 1`, where you will need to open your browser
60
+ manually to.
50
61
 
51
62
  ### **2. Manifest & Asset Resolution**
52
63
 
53
- The plugin automatically detects your manifest file (`module.json` or `system.json`) in the project **root** or `public/` folder.
64
+ The plugin automatically detects your manifest file (`module.json` or `system.json`) in the project
65
+ **root** or `public/` folder.
54
66
 
55
- This plugin shapes the output depending on your manifest; it tries to automatically discover the relevant files in the `root`, `source`, and `public` folders to build the output files. The `public` folder is defined by the Vite config file. The plugin determines the `source` directory based on your `lib.entry` path. For example, if your `lib.entry` is './mysource/package/main.js', the `mysource/` directory is considered your source directory.
67
+ This plugin shapes the output depending on your manifest; it tries to automatically discover the
68
+ relevant files in the `root`, `source`, and `public` folders to build the output files. The `public`
69
+ folder is defined by the Vite config file. The plugin determines the `source` directory based on
70
+ your `lib.entry` path. For example, if your `lib.entry` is './mysource/package/main.js', the
71
+ `mysource/` directory is considered your source directory.
56
72
 
57
- 💡 Your entry file should always import your main stylesheet; the manifest dictates how everything is named and output.
73
+ 💡 Your entry file should always import your main stylesheet; the manifest dictates how everything
74
+ is named and output.
58
75
 
59
76
  ### **3. ESModules, Scripts & Styles**
60
77
 
61
- `esmodules` and `scripts` declared in your manifest are automatically created from your `lib.entry`. Since Vite compiles the module, the plugin expects the `esmodules` or `scripts` entry in your manifest to only point to a single JavaScript file.
78
+ `esmodules` and `scripts` declared in your manifest are automatically created from your `lib.entry`.
79
+ Since Vite compiles the module, the plugin expects the `esmodules` or `scripts` entry in your
80
+ manifest to only point to a single JavaScript file.
62
81
 
63
- Stylesheets (CSS/SCSS/LESS) should be imported in your entry file; the plugin ensures they are outputted as the correct file.
82
+ Stylesheets (CSS/SCSS/LESS) should be imported in your entry file; the plugin ensures they are
83
+ outputted as the correct file.
64
84
 
65
85
  ### **4. Template Handling**
66
86
 
67
- Templates work in HMR properly on the development server; they are autodiscovered as discussed in [2. Manifest & Asset Resolution](#2-manifest--asset-resolution). The development server intercepts the websocket traffic and sends the local templates instead of Foundry VTT's, if present. e.g., a template request to `/systems/mysystem/tpl/character-header.hbs` might be rerouted to `public/tpl/character-header.hbs`. Folder structure inside your project is mirrored, apart from the `system`/`module` specific prefix.
87
+ Templates work in HMR properly on the development server; they are autodiscovered as discussed in
88
+ [2. Manifest & Asset Resolution](#2-manifest--asset-resolution). The development server intercepts
89
+ the websocket traffic and sends the local templates instead of Foundry VTT's, if present. e.g., a
90
+ template request to `/systems/mysystem/tpl/character-header.hbs` might be rerouted to
91
+ `public/tpl/character-header.hbs`. Folder structure inside your project is mirrored, apart from the
92
+ `system`/`module` specific prefix.
68
93
 
69
94
  ### **5. Language File Merging**
70
95
 
71
96
  Supports both complete and partial translation workflows:
72
97
 
73
- * **Complete files:** Place a complete JSON file (e.g., `public/lang/en.json`) and the plugin will copy it as-is.
74
- * **Partial files:** Place multiple JSONs inside `src/lang/en/` and the plugin merges them into one `lang/en.json` at build.
98
+ - **Complete files:** Place a complete JSON file (e.g., `public/lang/en.json`) and the plugin will
99
+ copy it as-is.
100
+ - **Partial files:** Place multiple JSONs inside `src/lang/en/` and the plugin merges them into one
101
+ `lang/en.json` at build.
75
102
 
76
103
  Merging follows your manifest’s declared language paths, searching in root or source directories.
77
104
 
78
105
  ⚠️ **Note:** HMR works for language files, but non-English locales may not reload as expected.
79
106
 
80
107
  ### **Example Project Structure**
108
+
81
109
  ```
82
110
  my-module/
83
111
  ├─ src/
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Plugin } from "vite";
2
2
 
3
3
  //#region src/index.d.ts
4
- declare function foundryVTTPlugin(): Plugin;
4
+ declare function foundryVTTPlugin(options?: {
5
+ buildPacks: boolean;
6
+ }): Plugin;
5
7
  //#endregion
6
8
  export { foundryVTTPlugin as default };
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import fs from "fs-extra";
2
2
  import posix from "path/posix";
3
3
  import dotenv from "dotenv";
4
4
  import { globSync } from "tinyglobby";
5
+ import { compilePack } from "@foundryvtt/foundryvtt-cli";
5
6
  import { Server } from "socket.io";
6
7
  import { io } from "socket.io-client";
7
8
 
@@ -95,26 +96,36 @@ function createPartialViteConfig(config) {
95
96
  const base = config.base ?? `/${context.manifest?.manifestType}s/${context.manifest?.id}/`;
96
97
  const useEsModules = context.manifest?.esmodules.length === 1;
97
98
  const formats = useEsModules ? ["es"] : ["umd"];
98
- const fileName = (useEsModules ? context.manifest?.esmodules[0] : context.manifest?.scripts?.[0]) ?? "bundle";
99
- if (fileName === "bundle") logger_default.warn("No output file specified in manifest, using default \"bundle\"");
99
+ const fileName = (useEsModules ? context.manifest?.esmodules[0] : context.manifest?.scripts?.[0]) ?? "scripts/bundle.js";
100
+ if (!(useEsModules || context.manifest?.scripts?.[0])) logger_default.warn("No output file specified in manifest, using default \"bundle\" in the \"scripts/\" folder");
100
101
  if (!context.manifest?.styles?.length) logger_default.warn("No CSS file found in manifest");
101
- const cssFileName = posix.parse(context.manifest?.styles[0] ?? "bundle.css").name;
102
+ const cssFileName = context.manifest?.styles[0] ?? "styles/bundle.css";
103
+ if (!context.manifest?.styles[0]) logger_default.warn("No output css file specified in manifest, using default \"bundle\" in the \"styles/\" folder");
102
104
  const foundryPort = context.env?.foundryPort ?? 3e4;
103
105
  const foundryUrl = context.env?.foundryUrl ?? "localhost";
104
106
  const entry = (config.build?.lib)?.entry;
105
107
  if (!entry) logger_default.fail("Entry must be specified in lib");
106
108
  if (typeof entry !== "string") logger_default.fail("Only a singular string entry is supported for build.lib.entry");
109
+ const isWatch = process.argv.includes("--watch") || !!config.build?.watch;
107
110
  return {
108
111
  base,
109
112
  build: {
110
- minify: "esbuild",
113
+ emptyOutDir: config.build?.emptyOutDir ?? !isWatch,
111
114
  lib: {
112
- cssFileName,
113
115
  entry,
114
- fileName,
115
116
  formats,
116
- name: context.manifest?.id
117
- }
117
+ name: context.manifest?.id ?? "bundle",
118
+ cssFileName: "bundle"
119
+ },
120
+ minify: "esbuild",
121
+ rollupOptions: { output: {
122
+ entryFileNames: fileName,
123
+ assetFileNames: (assetInfo) => {
124
+ const names = assetInfo.names ?? [];
125
+ if (names.some((n) => n.endsWith(".css"))) return cssFileName;
126
+ return "[name][extname]";
127
+ }
128
+ } }
118
129
  },
119
130
  define: { __FVTT_PLUGIN__: {
120
131
  id: context.manifest?.id,
@@ -133,85 +144,6 @@ function createPartialViteConfig(config) {
133
144
  };
134
145
  }
135
146
 
136
- //#endregion
137
- //#region src/server/hmr-client.ts
138
- var hmr_client_default = `
139
- if (import.meta.hot) {
140
- const FVTT_PLUGIN = __FVTT_PLUGIN__
141
-
142
- function refreshApplications(path = null) {
143
- // AppV1 refresh
144
- Object.values(foundry.ui.windows).forEach(app => app.render(true))
145
- // AppV2 refresh
146
- if (path)
147
- foundry.applications.instances.forEach(appV2 => {
148
- Object.values(appV2.constructor.PARTS ?? {}).forEach(part => {
149
- const templates = Array.isArray(part.templates) ? part.templates : []
150
- if (part.template === path || templates.includes(path)) appV2.render(true)
151
- })
152
- })
153
- else foundry.applications.instances.forEach(appV2 => appV2.render(true))
154
- }
155
-
156
- import.meta.hot.on('foundryvtt-template-update', ({ path }) => {
157
- game.socket.emit('template', path, response => {
158
- if (response.error) new Error(response.error)
159
- let template = undefined
160
- try {
161
- template = Handlebars.compile(response.html)
162
- } catch (error) {
163
- console.error(error)
164
- return
165
- }
166
- Handlebars.registerPartial(path, template)
167
- console.log(\`Vite | Retrieved and compiled template \${path}\`)
168
- refreshApplications(path)
169
- })
170
- })
171
-
172
- async function hmrLanguage(lang, targetObject = game.i18n.translations) {
173
- try {
174
- const languages = FVTT_PLUGIN.isSystem
175
- ? game.system.languages
176
- : game.modules.get(FVTT_PLUGIN.id)?.languages
177
- if (!languages) {
178
- console.warn(
179
- 'Vite | Got a HMR request to reload languages, however no languages were found.',
180
- )
181
- return
182
- }
183
- const langEntry = languages.find(l => l.lang === lang)
184
- if (!langEntry) {
185
- console.warn('Vite | Got an HMR request for an undefined language')
186
- return
187
- }
188
-
189
- const url = langEntry.path
190
- const resp = await fetch(url)
191
- if (!resp.ok) throw new Error('Failed to fetch language file!')
192
-
193
- const json = await resp.json()
194
-
195
- foundry.utils.mergeObject(targetObject, json)
196
- console.log(\`Vite | HMR: Reloaded language '\${lang}'\`)
197
- } catch (error) {
198
- console.error(\`Vite | HMR: Error reloading language '\${lang}' for \${FVTT_PLUGIN.id}\`, error);
199
- }
200
- }
201
-
202
- import.meta.hot.on('foundryvtt-language-update', async () => {
203
- const currentLang = game.i18n.lang
204
- const promises = []
205
- if (currentLang !== 'en') {
206
- promises.push(hmrLanguage('en', game.i18n._fallback))
207
- }
208
- promises.push(hmrLanguage(currentLang))
209
- await Promise.all(promises)
210
- refreshApplications()
211
- })
212
- } else console.error('Vite | HMR is disabled')
213
- //`;
214
-
215
147
  //#endregion
216
148
  //#region src/server/trackers/abstract-file-tracker.ts
217
149
  var AbstractFileTracker = class {
@@ -247,19 +179,6 @@ var AbstractFileTracker = class {
247
179
  }
248
180
  };
249
181
 
250
- //#endregion
251
- //#region src/server/trackers/handlebars-tracker.ts
252
- var HandlebarsTracker = class extends AbstractFileTracker {
253
- updateEvent = "foundryvtt-template-update";
254
- constructor() {
255
- super(context.config);
256
- }
257
- getEventData(changedPath, value) {
258
- return { path: value };
259
- }
260
- };
261
- const handlebarsTracker = new HandlebarsTracker();
262
-
263
182
  //#endregion
264
183
  //#region src/server/trackers/language-tracker.ts
265
184
  var LanguageTracker = class extends AbstractFileTracker {
@@ -445,6 +364,71 @@ function transform(dataMap) {
445
364
  return mergedData;
446
365
  }
447
366
 
367
+ //#endregion
368
+ //#region src/language/validator.ts
369
+ function validator() {
370
+ const manifest = context.manifest;
371
+ const baseLanguageData = loadLanguage("en", true);
372
+ if (baseLanguageData.size === 0) {
373
+ logger_default.error("Base language \"en\" not found or could not be loaded.");
374
+ return;
375
+ }
376
+ const base = flattenKeys(baseLanguageData.values().next().value);
377
+ for (const lang of manifest.languages) {
378
+ if (lang.lang === "en") continue;
379
+ const currentLanguageData = loadLanguage(lang.lang, true);
380
+ if (currentLanguageData.size === 0) {
381
+ console.warn(`Summary for language [${lang.lang}]: Could not be loaded.`);
382
+ continue;
383
+ }
384
+ const current = flattenKeys(currentLanguageData.values().next().value);
385
+ const missing = Object.keys(base).filter((key) => !(key in current));
386
+ const extra = Object.keys(current).filter((key) => !(key in base));
387
+ console.log(`Summary for language [${lang.lang}]:`);
388
+ if (missing.length) console.warn(`\tMissing keys: ${missing.length}`, missing.slice(0, 5));
389
+ if (extra.length) console.warn(`\tExtra keys: ${extra.length}`, extra.slice(0, 5));
390
+ if (!missing.length && !extra.length) console.log(" ✅ All keys match.");
391
+ }
392
+ }
393
+
394
+ //#endregion
395
+ //#region src/packs/compile-packs.ts
396
+ async function compileManifestPacks() {
397
+ if (!context.manifest?.packs) return;
398
+ for (const pack of context.manifest.packs) {
399
+ const srcCandidates = [posix.resolve(path_utils_default.getSourceDirectory(), pack.path), posix.resolve(path_utils_default.getRoot(), pack.path)];
400
+ const dest = posix.resolve(path_utils_default.getOutDir(), pack.path);
401
+ const chosenSrc = srcCandidates.find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isDirectory());
402
+ if (!chosenSrc) {
403
+ logger_default.warn(`Pack path not found for ${pack.path}, skipped.`);
404
+ continue;
405
+ }
406
+ const entries = fs.readdirSync(chosenSrc, {
407
+ recursive: true,
408
+ encoding: "utf8"
409
+ });
410
+ const hasYaml = entries.some((entry) => entry.endsWith(".yaml") || entry.endsWith(".yml"));
411
+ await compilePack(chosenSrc, dest, {
412
+ yaml: hasYaml,
413
+ recursive: true
414
+ });
415
+ logger_default.info(`Compiled pack ${pack.path} (${hasYaml ? "YAML" : "JSON"}) from ${chosenSrc}`);
416
+ }
417
+ }
418
+
419
+ //#endregion
420
+ //#region src/server/trackers/handlebars-tracker.ts
421
+ var HandlebarsTracker = class extends AbstractFileTracker {
422
+ updateEvent = "foundryvtt-template-update";
423
+ constructor() {
424
+ super(context.config);
425
+ }
426
+ getEventData(changedPath, value) {
427
+ return { path: value };
428
+ }
429
+ };
430
+ const handlebarsTracker = new HandlebarsTracker();
431
+
448
432
  //#endregion
449
433
  //#region src/server/http-middleware.ts
450
434
  function httpMiddlewareHook(server) {
@@ -523,35 +507,87 @@ function setupDevServer(server) {
523
507
  }
524
508
 
525
509
  //#endregion
526
- //#region src/language/validator.ts
527
- function validator() {
528
- const manifest = context.manifest;
529
- const baseLanguageData = loadLanguage("en", true);
530
- if (baseLanguageData.size === 0) {
531
- logger_default.error("Base language \"en\" not found or could not be loaded.");
532
- return;
533
- }
534
- const base = flattenKeys(baseLanguageData.values().next().value);
535
- for (const lang of manifest.languages) {
536
- if (lang.lang === "en") continue;
537
- const currentLanguageData = loadLanguage(lang.lang, true);
538
- if (currentLanguageData.size === 0) {
539
- console.warn(`Summary for language [${lang.lang}]: Could not be loaded.`);
540
- continue;
541
- }
542
- const current = flattenKeys(currentLanguageData.values().next().value);
543
- const missing = Object.keys(base).filter((key) => !(key in current));
544
- const extra = Object.keys(current).filter((key) => !(key in base));
545
- console.log(`Summary for language [${lang.lang}]:`);
546
- if (missing.length) console.warn(`\tMissing keys: ${missing.length}`, missing.slice(0, 5));
547
- if (extra.length) console.warn(`\tExtra keys: ${extra.length}`, extra.slice(0, 5));
548
- if (!missing.length && !extra.length) console.log(" ✅ All keys match.");
549
- }
550
- }
510
+ //#region src/server/hmr-client.ts
511
+ var hmr_client_default = `
512
+ if (import.meta.hot) {
513
+ const FVTT_PLUGIN = __FVTT_PLUGIN__
514
+
515
+ function refreshApplications(path = null) {
516
+ // AppV1 refresh
517
+ Object.values(foundry.ui.windows).forEach(app => app.render(true))
518
+ // AppV2 refresh
519
+ if (path)
520
+ foundry.applications.instances.forEach(appV2 => {
521
+ Object.values(appV2.constructor.PARTS ?? {}).forEach(part => {
522
+ const templates = Array.isArray(part.templates) ? part.templates : []
523
+ if (part.template === path || templates.includes(path)) appV2.render(true)
524
+ })
525
+ })
526
+ else foundry.applications.instances.forEach(appV2 => appV2.render(true))
527
+ }
528
+
529
+ import.meta.hot.on('foundryvtt-template-update', ({ path }) => {
530
+ game.socket.emit('template', path, response => {
531
+ if (response.error) new Error(response.error)
532
+ let template = undefined
533
+ try {
534
+ template = Handlebars.compile(response.html)
535
+ } catch (error) {
536
+ console.error(error)
537
+ return
538
+ }
539
+ Handlebars.registerPartial(path, template)
540
+ console.log(\`Vite | Retrieved and compiled template \${path}\`)
541
+ refreshApplications(path)
542
+ })
543
+ })
544
+
545
+ async function hmrLanguage(lang, targetObject = game.i18n.translations) {
546
+ try {
547
+ const languages = FVTT_PLUGIN.isSystem
548
+ ? game.system.languages
549
+ : game.modules.get(FVTT_PLUGIN.id)?.languages
550
+ if (!languages) {
551
+ console.warn(
552
+ 'Vite | Got a HMR request to reload languages, however no languages were found.',
553
+ )
554
+ return
555
+ }
556
+ const langEntry = languages.find(l => l.lang === lang)
557
+ if (!langEntry) {
558
+ console.warn('Vite | Got an HMR request for an undefined language')
559
+ return
560
+ }
561
+
562
+ const url = langEntry.path
563
+ const resp = await fetch(url)
564
+ if (!resp.ok) throw new Error('Failed to fetch language file!')
565
+
566
+ const json = await resp.json()
567
+
568
+ foundry.utils.mergeObject(targetObject, json)
569
+ console.log(\`Vite | HMR: Reloaded language '\${lang}'\`)
570
+ } catch (error) {
571
+ console.error(\`Vite | HMR: Error reloading language '\${lang}' for \${FVTT_PLUGIN.id}\`, error);
572
+ }
573
+ }
574
+
575
+ import.meta.hot.on('foundryvtt-language-update', async () => {
576
+ const currentLang = game.i18n.lang
577
+ const promises = []
578
+ if (currentLang !== 'en') {
579
+ promises.push(hmrLanguage('en', game.i18n._fallback))
580
+ }
581
+ promises.push(hmrLanguage(currentLang))
582
+ await Promise.all(promises)
583
+ refreshApplications()
584
+ })
585
+ } else console.error('Vite | HMR is disabled')
586
+ //`;
551
587
 
552
588
  //#endregion
553
589
  //#region src/index.ts
554
- function foundryVTTPlugin() {
590
+ function foundryVTTPlugin(options = { buildPacks: true }) {
555
591
  context.env = loadEnv();
556
592
  return {
557
593
  name: "vite-plugin-fvtt",
@@ -577,12 +613,13 @@ function foundryVTTPlugin() {
577
613
  }
578
614
  const languages = context.manifest?.languages ?? [];
579
615
  if (languages.length > 0) for (const language of languages) {
580
- if (path_utils_default.getOutDirFile(language.path)) continue;
616
+ if (path_utils_default.getPublicDirFile(language.path)) continue;
581
617
  getLocalLanguageFiles(language.lang).forEach((langFile) => this.addWatchFile(langFile));
582
618
  const languageDataRaw = loadLanguage(language.lang);
583
619
  const languageData = transform(languageDataRaw);
584
620
  fs.writeJSONSync(posix.join(outDir, language.path), languageData);
585
621
  }
622
+ if (options.buildPacks) await compileManifestPacks();
586
623
  },
587
624
  closeBundle() {
588
625
  const languages = context.manifest?.languages ?? [];
@@ -590,7 +627,7 @@ function foundryVTTPlugin() {
590
627
  },
591
628
  load(id) {
592
629
  const config = context.config;
593
- const jsFileName = config.build.lib.fileName;
630
+ const jsFileName = (config.build.rollupOptions?.output).entryFileNames;
594
631
  if (id === jsFileName || id === `/${jsFileName}`) {
595
632
  const entryPath = posix.resolve(config.build.lib.entry);
596
633
  const viteId = `/@fs/${entryPath}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-fvtt",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "A Vite plugin for module and system development for Foundry VTT",
5
5
  "keywords": [
6
6
  "vite",
@@ -22,6 +22,7 @@
22
22
  "types": "dist/index.d.ts",
23
23
  "files": [
24
24
  "dist",
25
+ "CHANGELOG.md",
25
26
  "LICENSE",
26
27
  "README.md"
27
28
  ],
@@ -41,6 +42,7 @@
41
42
  "vite": "*"
42
43
  },
43
44
  "dependencies": {
45
+ "@foundryvtt/foundryvtt-cli": "^3.0.0",
44
46
  "dotenv": "^17.2.1",
45
47
  "fs-extra": "^11.3.1",
46
48
  "socket.io": "^4.8.1",