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 +70 -0
- package/README.md +50 -22
- package/dist/index.d.ts +3 -1
- package/dist/index.js +165 -128
- package/package.json +3 -1
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
|
|
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
|
|
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
|
|
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
|
|
14
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`.
|
|
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
|
|
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
|
|
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
|
-
|
|
74
|
-
|
|
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
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 (
|
|
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 =
|
|
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
|
-
|
|
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/
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|