foundry-vtt-react 0.1.0 → 0.1.5
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/LICENSE +21 -0
- package/README.md +233 -11
- package/dist/{chunk-U6WX7YSY.mjs → chunk-7ZJUEWDX.mjs} +1 -1
- package/dist/chunk-7ZJUEWDX.mjs.map +1 -0
- package/dist/index.d.mts +19 -4
- package/dist/index.mjs +33 -11
- package/dist/index.mjs.map +1 -1
- package/dist/util/dev-setup.d.mts +4 -0
- package/dist/util/dev-setup.mjs +1 -1
- package/dist/vite/index.d.mts +38 -0
- package/dist/vite/index.mjs +93 -0
- package/dist/vite/index.mjs.map +1 -0
- package/package.json +30 -5
- package/dist/chunk-U6WX7YSY.mjs.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tim Sandberg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,26 +1,47 @@
|
|
|
1
1
|
# Foundry VTT React
|
|
2
2
|
|
|
3
|
-
This package provides extensions of various Foundry VTT classes to support React applications
|
|
3
|
+
This package provides a development and build harness for creating React applications within Foundry. It also includes extensions of various Foundry VTT classes to support building React applications inside Foundry VTT. This is a bit experimental, but enables a modern JS workflow and if you know and enjoy React workflows with HMR/fast-refresh, this should have you flying in no time.
|
|
4
|
+
|
|
5
|
+
**Disclaimers:**
|
|
6
|
+
- Only tested against Foundry v13
|
|
7
|
+
- Not a Foundry module, but a build dependency to add to your module's build workflow and dev setup
|
|
8
|
+
- Relies on Vitejs building.
|
|
9
|
+
- TypeScript friendly!
|
|
4
10
|
|
|
5
11
|
## Installation
|
|
6
12
|
|
|
7
|
-
|
|
13
|
+
```bash
|
|
14
|
+
npm install foundry-vtt-react
|
|
15
|
+
# or: pnpm add foundry-vtt-react
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`react` and `react-dom` (v19) are peer dependencies — install them in your module if you haven't already:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install react react-dom
|
|
22
|
+
```
|
|
8
23
|
|
|
9
|
-
|
|
10
|
-
|
|
24
|
+
## Supported Foundry classes
|
|
25
|
+
|
|
26
|
+
- `ReactApplicationV2` — a base `ApplicationV2` that renders a React component instead of a Handlebars template.
|
|
27
|
+
- `ReactActorSheetV2` — an `ActorSheetV2` that renders with React, letting your component react to document changes through the native sheet lifecycle.
|
|
28
|
+
|
|
29
|
+
Both are produced by the same `ReactApplicationMixin`, so they share the options and lifecycle described below.
|
|
11
30
|
|
|
12
31
|
## Usage
|
|
13
32
|
|
|
14
|
-
There are two
|
|
33
|
+
There are two pieces to developing React applications in Foundry:
|
|
15
34
|
|
|
16
|
-
1. Creating
|
|
17
|
-
2. Configuring
|
|
35
|
+
1. Creating a React-powered application instance and passing in your React component.
|
|
36
|
+
2. Configuring a Vite dev server to work with your local Foundry instance (see [Development setup](#development-setup-with-vite)).
|
|
18
37
|
|
|
19
|
-
### Creating a ReactApplicationV2
|
|
38
|
+
### Creating a ReactApplicationV2 instance
|
|
20
39
|
|
|
21
|
-
|
|
40
|
+
Instantiate `ReactApplicationV2` with your React component plus any initial props and window options:
|
|
41
|
+
|
|
42
|
+
```jsx
|
|
43
|
+
import { ReactApplicationV2 } from "foundry-vtt-react";
|
|
22
44
|
|
|
23
|
-
```javascript
|
|
24
45
|
// Basic component
|
|
25
46
|
function MyReactComponent(props) {
|
|
26
47
|
return <div>Hello, {props.data}!</div>;
|
|
@@ -36,4 +57,205 @@ const app = new ReactApplicationV2({
|
|
|
36
57
|
app.render(true);
|
|
37
58
|
```
|
|
38
59
|
|
|
39
|
-
|
|
60
|
+
A React component rendered inside a Foundry application window
|
|
61
|
+
|
|
62
|
+
**Constructor options**
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
| Option | Type | Description |
|
|
66
|
+
| -------------- | --------------------- | ---------------------------------------------------------------------------------------- |
|
|
67
|
+
| `reactApp` | `React.ComponentType` | The component mounted into the application window. |
|
|
68
|
+
| `initialProps` | `object` (optional) | Props passed to `reactApp` on mount. Also reachable via `_prepareContext` (see below). |
|
|
69
|
+
| ...options | `ApplicationV2` | Any standard `ApplicationV2` options (`window`, `position`, `classes`, `actions`, etc.). |
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
### Building a React actor sheet
|
|
73
|
+
|
|
74
|
+
Subclass `ReactActorSheetV2`, set `reactApp`, and register it as the sheet for your actor type. Override `_prepareContext` to choose exactly which props your component receives — this is also where you hand your component the `[ContextConnector](#reacting-to-foundry-updates-with-contextconnector)` so it can subscribe to live document updates:
|
|
75
|
+
|
|
76
|
+
```jsx
|
|
77
|
+
import { ReactActorSheetV2 } from "foundry-vtt-react";
|
|
78
|
+
import MySheetApp from "./MySheetApp";
|
|
79
|
+
|
|
80
|
+
class MyActorSheet extends ReactActorSheetV2 {
|
|
81
|
+
reactApp = MySheetApp;
|
|
82
|
+
|
|
83
|
+
static DEFAULT_OPTIONS = {
|
|
84
|
+
window: { title: "My Sheet", resizable: true },
|
|
85
|
+
position: { width: 625, height: 750 },
|
|
86
|
+
classes: ["my-sheet"],
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
async _prepareContext(options) {
|
|
90
|
+
const context = await super._prepareContext(options);
|
|
91
|
+
// Pick the props your React app receives:
|
|
92
|
+
context.initialProps = {
|
|
93
|
+
actor: context.document,
|
|
94
|
+
source: context.source,
|
|
95
|
+
contextConnector: this.contextConnector, // for live updates
|
|
96
|
+
};
|
|
97
|
+
return context;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Register it like any other sheet, e.g.:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
foundry.documents.collections.Actors.registerSheet("my-module", MyActorSheet, {
|
|
106
|
+
types: ["character"],
|
|
107
|
+
makeDefault: true,
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Reacting to Foundry updates with ContextConnector
|
|
112
|
+
|
|
113
|
+
Every React application instance owns a `ContextConnector` at `this.contextConnector`. On **every** render, the mixin calls `contextConnector.publishContext(context)` with the prepared context (which, for a sheet, includes the updated `document`). Pass the connector into your component (via `initialProps`, as shown above) and subscribe to those updates so React re-renders when the Foundry document changes.
|
|
114
|
+
|
|
115
|
+
`onUpdate(callback)` returns a **disposer** function, ideal for `useEffect` cleanup:
|
|
116
|
+
|
|
117
|
+
```jsx
|
|
118
|
+
import { useEffect, useState } from "react";
|
|
119
|
+
|
|
120
|
+
function MySheetApp({ actor: initialActor, contextConnector }) {
|
|
121
|
+
const [actor, setActor] = useState(initialActor);
|
|
122
|
+
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
// Re-render whenever Foundry re-renders the sheet
|
|
125
|
+
const off = contextConnector.onUpdate(({ document }) => {
|
|
126
|
+
setActor(document);
|
|
127
|
+
});
|
|
128
|
+
return off; // unsubscribe on unmount
|
|
129
|
+
}, [contextConnector]);
|
|
130
|
+
|
|
131
|
+
return <h1>{actor.name}</h1>;
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
You can debounce noisy update streams with Foundry's helper:
|
|
136
|
+
|
|
137
|
+
```jsx
|
|
138
|
+
const handleUpdate = foundry.utils.debounce(({ document }) => {
|
|
139
|
+
setActor(document);
|
|
140
|
+
}, 200);
|
|
141
|
+
const off = contextConnector.onUpdate(handleUpdate);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`ContextConnector<T>` **API**
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
| Method | Returns | Description |
|
|
148
|
+
| ------------------------- | ------------ | ------------------------------------------------------------------- |
|
|
149
|
+
| `onUpdate(cb)` | `() => void` | Subscribe to context updates. Returns a disposer that unsubscribes. |
|
|
150
|
+
| `tearDown(cb)` | `void` | Unsubscribe a callback previously passed to `onUpdate`. |
|
|
151
|
+
| `on(event, cb)` | `() => void` | Subscribe to a custom event. Returns a disposer. |
|
|
152
|
+
| `off(event, cb)` | `void` | Unsubscribe a callback from a custom event. |
|
|
153
|
+
| `publishContext(context)` | `void` | Emit a context update. Called for you by the mixin on each render. |
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
> Use the returned disposer **or** `tearDown(cb)` to clean up — either removes the listener.
|
|
157
|
+
|
|
158
|
+
## Development setup with Vite
|
|
159
|
+
|
|
160
|
+
For a fast dev loop with React Fast Refresh inside Foundry, add the `foundry-vtt-react/vite` plugin. Your manifest's `esmodules` points at `dist/main.js`: in production that's your built bundle, and in dev the plugin's middleware serves that same URL with the Fast Refresh preamble + a dynamic import of your real entry — so there's no shim file and no hand-written Vite config to maintain.
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
my-module/
|
|
164
|
+
├─ module.json # esmodules: ["dist/main.js"]
|
|
165
|
+
├─ vite.config.ts
|
|
166
|
+
└─ src/
|
|
167
|
+
├─ main.ts # real entry: Hooks, registerSheet, … (build input)
|
|
168
|
+
└─ MySheetApp.tsx # your React component(s)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**1. Add the plugin** — it derives everything from your `module.json` `id`:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
// vite.config.ts
|
|
175
|
+
import { defineConfig } from "vite";
|
|
176
|
+
import react from "@vitejs/plugin-react";
|
|
177
|
+
import foundryReact from "foundry-vtt-react/vite";
|
|
178
|
+
|
|
179
|
+
export default defineConfig({ plugins: [react(), foundryReact()] });
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**2. Add scripts** — `"dev": "vite"` for the HMR server, `"build": "vite build"` for production.
|
|
183
|
+
|
|
184
|
+
**3. Make the module visible to Foundry** — symlink (or copy) your module folder into Foundry's `Data/modules/`. With Docker, mount it instead:
|
|
185
|
+
|
|
186
|
+
> ```yaml
|
|
187
|
+
> foundry:
|
|
188
|
+
> image: felddy/foundryvtt:13
|
|
189
|
+
> volumes:
|
|
190
|
+
> - /path/to/my-module:/data/Data/modules/my-module
|
|
191
|
+
> ```
|
|
192
|
+
|
|
193
|
+
**4. Run it** — start Foundry (`:30000`), then `npm run dev` and open the Vite server (`:30001`). Edits to your components hot-reload. For production, `npm run build` ships `dist/`; the dev middleware isn't involved.
|
|
194
|
+
|
|
195
|
+
`vite` and `@vitejs/plugin-react` are **optional peer dependencies** — needed only for this plugin, not the runtime classes (you already have them for any React + Vite setup).
|
|
196
|
+
|
|
197
|
+
### Plugin options
|
|
198
|
+
|
|
199
|
+
All options are optional:
|
|
200
|
+
|
|
201
|
+
| Option | Default | Description |
|
|
202
|
+
| --------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
203
|
+
| `appId` | the `id` read from `./module.json` | Your module's `id`. Used to build served paths and the proxy rule. |
|
|
204
|
+
| `entry` | `"src/main.ts"` | Your real app entry / build input, relative to the project root. |
|
|
205
|
+
| `foundryUrl` | `"http://localhost:30000"` | The local Foundry server that non-bundle requests are proxied to. |
|
|
206
|
+
| `port` | `30001` | The Vite dev server port. |
|
|
207
|
+
| `manifestEntry` | basename of `module.json` `esmodules[0]`, else `"main.js"` | The bundle filename Foundry requests (where the dev preamble is served). |
|
|
208
|
+
|
|
209
|
+
### What it expands to
|
|
210
|
+
|
|
211
|
+
For a module whose `id` is `my-module`, `foundryReact()` contributes the Vite config you'd otherwise hand-write — each value applied only when you haven't set it yourself, so your own config always wins:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
{
|
|
215
|
+
base: "/modules/my-module/dist",
|
|
216
|
+
root: "src",
|
|
217
|
+
// react/react-dom forced to a single copy — without this, a linked or git-installed
|
|
218
|
+
// foundry-vtt-react pulls a second React and every hook throws "Invalid hook call".
|
|
219
|
+
resolve: { dedupe: ["react", "react-dom"] },
|
|
220
|
+
server: {
|
|
221
|
+
port: 30001,
|
|
222
|
+
proxy: {
|
|
223
|
+
"^(?!/modules/my-module/dist)": "http://localhost:30000", // non-bundle requests → Foundry
|
|
224
|
+
"/socket.io": { target: "ws://localhost:30000", ws: true },
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
build: {
|
|
228
|
+
outDir: "<root>/dist",
|
|
229
|
+
emptyOutDir: true,
|
|
230
|
+
rollupOptions: {
|
|
231
|
+
input: "<root>/src/main.ts", // your `entry`
|
|
232
|
+
output: { entryFileNames: "[name].js", assetFileNames: "[name].[ext]", format: "es" },
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
In dev it also serves the manifest URL (`/modules/my-module/dist/main.js`) with the module that replaces the old `src/main.js` shim:
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
// Fast Refresh preamble (reused from @vitejs/plugin-react; base derived from your config)
|
|
242
|
+
import { injectIntoGlobalHook } from "/modules/my-module/dist/@react-refresh";
|
|
243
|
+
injectIntoGlobalHook(window);
|
|
244
|
+
window.$RefreshReg$ = () => {};
|
|
245
|
+
window.$RefreshSig$ = () => (type) => type;
|
|
246
|
+
import("/modules/my-module/dist/main.ts"); // dynamic, so the preamble runs first
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
> **Migrating from `devSetup`:** `devSetup` is **deprecated**. Delete your `src/main.js` shim and the hand-written `base`/`server`/`build` config, then add `foundryReact()` — it reproduces the same behavior, deriving the preamble and paths from your resolved Vite config.
|
|
250
|
+
|
|
251
|
+
## Exports
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
import {
|
|
255
|
+
ReactApplicationV2,
|
|
256
|
+
ReactActorSheetV2,
|
|
257
|
+
ContextConnector,
|
|
258
|
+
devSetup,
|
|
259
|
+
} from "foundry-vtt-react";
|
|
260
|
+
```
|
|
261
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/util/logger.ts","../lib/util/dev-setup.ts"],"sourcesContent":["const logger =\n (namespace: string): ((message: string) => void) =>\n (message: string) => {\n console.log(`%c[foundry-vtt-react][${namespace}]`, \"color: tomato;\", message);\n };\n\nexport default logger;\n","import logger from \"../util/logger.js\";\nconst log = logger(\"dev-setup\");\n\n/**\n * devSetup.js\n *\n * Utility function to set up development environment for React applications in Foundry VTT. Used in conjunction\n * with a Vite development server, this function injects necessary scripts to enable React Fast Refresh and loads\n * the React application entrypoint.\n *\n * @deprecated Use the `foundry-vtt-react/vite` plugin instead. It derives the refresh preamble and paths\n * from your Vite config (no hand-maintained paths) and serves the dev entry via dev-server middleware, so\n * the `src/main.js` shim that calls `devSetup` is no longer needed. This function will be removed in a future major.\n *\n * @param {string} appId - The application ID used to construct module paths, typically the `id` value in `module.json`.\n * @param {string} entrypoint - The path to the React application entrypoint script relative to the module root. This should match\n * the entrypoint defined in the Vite configuration.\n */\nexport function devSetup(appId: string, entrypoint: string) {\n const refreshScriptId = `foundry-react-refresh-script-${appId}`;\n\n if (document.getElementById(refreshScriptId)) {\n log(\"Script tag already exists, not adding again\");\n return;\n } else {\n log(\"Adding script tag for react refresh\");\n\n const scriptInner = `\n import { injectIntoGlobalHook } from \"/modules/${appId}/dist/@react-refresh\";\n injectIntoGlobalHook(window);\n window.$RefreshReg$ = () => {};\n window.$RefreshSig$ = () => (type) => type;\n `;\n\n const tag = document.createElement(\"script\");\n tag.type = \"module\";\n tag.id = refreshScriptId;\n tag.innerHTML = scriptInner;\n document.head.prepend(tag);\n }\n\n const devEntrypointId = `foundry-react-dev-entrypoint-${appId}`;\n if (document.getElementById(devEntrypointId)) {\n log(\"Dev entrypoint script tag already exists, not adding again\");\n return;\n } else {\n const mainScript = document.createElement(\"script\");\n mainScript.type = \"module\";\n mainScript.src = `/modules/${appId}${entrypoint.startsWith(\"/\") ? \"\" : \"/\"}${entrypoint}`;\n document.body.appendChild(mainScript);\n }\n}\n"],"mappings":";AAAA,IAAM,SACJ,CAAC,cACD,CAAC,YAAoB;AACnB,UAAQ,IAAI,yBAAyB,SAAS,KAAK,kBAAkB,OAAO;AAC9E;AAEF,IAAO,iBAAQ;;;ACLf,IAAM,MAAM,eAAO,WAAW;AAiBvB,SAAS,SAAS,OAAe,YAAoB;AAC1D,QAAM,kBAAkB,gCAAgC,KAAK;AAE7D,MAAI,SAAS,eAAe,eAAe,GAAG;AAC5C,QAAI,6CAA6C;AACjD;AAAA,EACF,OAAO;AACL,QAAI,qCAAqC;AAEzC,UAAM,cAAc;AAAA,uDAC+B,KAAK;AAAA;AAAA;AAAA;AAAA;AAMxD,UAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,QAAI,OAAO;AACX,QAAI,KAAK;AACT,QAAI,YAAY;AAChB,aAAS,KAAK,QAAQ,GAAG;AAAA,EAC3B;AAEA,QAAM,kBAAkB,gCAAgC,KAAK;AAC7D,MAAI,SAAS,eAAe,eAAe,GAAG;AAC5C,QAAI,4DAA4D;AAChE;AAAA,EACF,OAAO;AACL,UAAM,aAAa,SAAS,cAAc,QAAQ;AAClD,eAAW,OAAO;AAClB,eAAW,MAAM,YAAY,KAAK,GAAG,WAAW,WAAW,GAAG,IAAI,KAAK,GAAG,GAAG,UAAU;AACvF,aAAS,KAAK,YAAY,UAAU;AAAA,EACtC;AACF;","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
export { devSetup } from './util/dev-setup.mjs';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Event-based bridge for pushing Foundry context updates into React.
|
|
5
|
+
*
|
|
6
|
+
* A `ReactApplicationMixin` instance creates one connector and calls
|
|
7
|
+
* {@link ContextConnector.publishContext} on every render. React components
|
|
8
|
+
* subscribe with {@link ContextConnector.onUpdate} to re-render on changes.
|
|
9
|
+
*/
|
|
3
10
|
declare class ContextConnector<T> extends EventTarget {
|
|
11
|
+
#private;
|
|
4
12
|
static UPDATE: string;
|
|
5
|
-
constructor();
|
|
6
13
|
publishContext(context: T): void;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Subscribe to an event. Returns a disposer that removes the listener,
|
|
16
|
+
* convenient for React `useEffect` cleanup.
|
|
17
|
+
*/
|
|
18
|
+
on(event: string, callback: (data: T) => void): () => void;
|
|
19
|
+
/** Subscribe to context updates. Returns a disposer. */
|
|
20
|
+
onUpdate(callback: (data: T) => void): () => void;
|
|
21
|
+
/** Remove a listener previously added with {@link on}. */
|
|
22
|
+
off(event: string, callback: (data: T) => void): void;
|
|
23
|
+
/** Remove a context-update listener previously added with {@link onUpdate}. */
|
|
24
|
+
tearDown(callback: (data: T) => void): void;
|
|
10
25
|
}
|
|
11
26
|
|
|
12
27
|
declare const ReactApplicationV2_base: {
|
package/dist/index.mjs
CHANGED
|
@@ -1,26 +1,48 @@
|
|
|
1
1
|
import {
|
|
2
2
|
devSetup
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7ZJUEWDX.mjs";
|
|
4
4
|
|
|
5
5
|
// lib/context-connector.ts
|
|
6
6
|
var ContextConnector = class _ContextConnector extends EventTarget {
|
|
7
7
|
static UPDATE = "contextUpdate";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Maps each subscriber callback to the wrapper actually registered with
|
|
10
|
+
* `addEventListener`, so `off`/`tearDown` can remove it by the original
|
|
11
|
+
* callback reference.
|
|
12
|
+
*/
|
|
13
|
+
#wrappers = /* @__PURE__ */ new Map();
|
|
11
14
|
publishContext(context) {
|
|
12
|
-
this.dispatchEvent(
|
|
15
|
+
this.dispatchEvent(
|
|
16
|
+
new CustomEvent(_ContextConnector.UPDATE, { detail: context })
|
|
17
|
+
);
|
|
13
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Subscribe to an event. Returns a disposer that removes the listener,
|
|
21
|
+
* convenient for React `useEffect` cleanup.
|
|
22
|
+
*/
|
|
14
23
|
on(event, callback) {
|
|
15
|
-
this.
|
|
16
|
-
callback(e.detail);
|
|
17
|
-
|
|
24
|
+
if (!this.#wrappers.has(callback)) {
|
|
25
|
+
const wrapper = (e) => callback(e.detail);
|
|
26
|
+
this.#wrappers.set(callback, wrapper);
|
|
27
|
+
this.addEventListener(event, wrapper);
|
|
28
|
+
}
|
|
29
|
+
return () => this.off(event, callback);
|
|
18
30
|
}
|
|
31
|
+
/** Subscribe to context updates. Returns a disposer. */
|
|
19
32
|
onUpdate(callback) {
|
|
20
|
-
this.on(_ContextConnector.UPDATE, callback);
|
|
33
|
+
return this.on(_ContextConnector.UPDATE, callback);
|
|
34
|
+
}
|
|
35
|
+
/** Remove a listener previously added with {@link on}. */
|
|
36
|
+
off(event, callback) {
|
|
37
|
+
const wrapper = this.#wrappers.get(callback);
|
|
38
|
+
if (wrapper) {
|
|
39
|
+
this.removeEventListener(event, wrapper);
|
|
40
|
+
this.#wrappers.delete(callback);
|
|
41
|
+
}
|
|
21
42
|
}
|
|
22
|
-
|
|
23
|
-
|
|
43
|
+
/** Remove a context-update listener previously added with {@link onUpdate}. */
|
|
44
|
+
tearDown(callback) {
|
|
45
|
+
this.off(_ContextConnector.UPDATE, callback);
|
|
24
46
|
}
|
|
25
47
|
};
|
|
26
48
|
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/context-connector.ts","../lib/util/mount-app.tsx","../lib/react-application-mixin.ts","../lib/react-application-v2.ts","../lib/react-actor-sheet-v2.ts"],"sourcesContent":["export class ContextConnector<T> extends EventTarget {\n static UPDATE = \"contextUpdate\";\n\n constructor() {\n super();\n }\n\n publishContext(context: T) {\n this.dispatchEvent(new CustomEvent(\"contextUpdate\", { detail: context }));\n }\n\n on(event: string, callback: (data: T) => void) {\n this.addEventListener(event, (e: CustomEventInit<T>) => {\n callback(e.detail!);\n });\n }\n\n onUpdate(callback: (data: T) => void) {\n this.on(ContextConnector.UPDATE, callback);\n }\n\n tearDown(fn: (data: T) => void) {\n this.removeEventListener(ContextConnector.UPDATE, fn as EventListener);\n }\n}\n","import { createRoot } from \"react-dom/client\";\n\nexport function mountApp({\n App,\n element,\n initialProps = {},\n innerSelector,\n}: {\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n App: React.ComponentType<any>;\n element: Element;\n initialProps?: {};\n innerSelector: string;\n}) {\n const root = createRoot(element);\n root.render(\n <div id={innerSelector}>\n <App {...initialProps} />\n </div>\n );\n}\n","import { ContextConnector } from \"./context-connector\";\nimport { mountApp } from \"./util/mount-app\";\n\n/**\n * A mixin that integrates React components with Foundry VTT's Application class.\n * This mixin enables the use of React applications within Foundry VTT's application framework.\n *\n * @param superclass - The base class to extend, typically a Foundry VTT Application class\n * @returns A class that extends the superclass with React integration capabilities\n *\n * @remarks\n * This mixin provides the following features:\n * - Mounting React components within Foundry VTT applications\n * - Context management through ContextConnector\n * - Automatic cleanup and rendering lifecycle management\n * - Default window options for position and configuration\n *\n * @example\n * ```typescript\n * class MyReactApp extends ReactApplicationMixin(Application) {\n * constructor(options) {\n * super({\n * reactApp: MyReactComponent,\n * initialProps: { data: \"example\" },\n * ...options\n * });\n * }\n * }\n * ```\n */\n\ntype ReactApplicationProps = {\n reactApp: React.ComponentType<any>;\n initialProps?: Record<string, any>;\n};\n\nfunction ReactApplicationMixin(Superclass: any) {\n return class ReactApplication extends Superclass {\n reactApp: React.ComponentType<any>;\n uuid = foundry.utils.randomID(12);\n rootId = `react-app-root-${this.uuid}`;\n innerSelector = `react-application-inner-${this.rootId}`;\n contextConnector: ContextConnector<any>;\n\n static DEFAULT_OPTIONS = {\n position: {\n width: 400,\n height: 300,\n },\n window: {\n title: \"Hello React-powered Foundry applications\",\n resizable: true,\n minimizable: true,\n },\n };\n\n initialProps = {};\n\n constructor({ reactApp, initialProps, ...options }: ReactApplicationProps & any) {\n super(options);\n this.reactApp = reactApp;\n this.contextConnector = new ContextConnector();\n this.initialProps = initialProps || {};\n }\n\n get appIsRendered() {\n return !!document.querySelector(`#${this.innerSelector}`);\n }\n\n async _onRender(context: any, options: any) {\n await super._onRender(context, options);\n const el = this.element.querySelectorAll(`#${this.rootId}`);\n\n if (el && !this.appIsRendered) {\n mountApp({\n App: this.reactApp,\n element: el[0],\n initialProps: context.initialProps,\n innerSelector: this.innerSelector,\n });\n }\n this.contextConnector.publishContext(context);\n }\n\n _replaceHTML(result: HTMLElement, content: HTMLElement) {\n if (!this.appIsRendered) {\n content.appendChild(result);\n }\n }\n\n async _prepareContext(options: any) {\n const context = (await super._prepareContext(options)) as any;\n context.initialProps = this.initialProps;\n return context;\n }\n\n async _renderHTML() {\n const tempEl = document.createElement(\"div\");\n tempEl.id = this.rootId;\n tempEl.innerHTML = `<span>Uh oh, something went wrong</span>`;\n return tempEl;\n }\n };\n}\n\nexport default ReactApplicationMixin;\n","import ReactApplicationMixin from \"./react-application-mixin\";\n\n/**\n * A Foundry VTT Application class that integrates React components with the Foundry application framework.\n * Extends ApplicationV2 to provide seamless React app mounting and rendering capabilities.\n *\n * @class FoundryReactApplication\n * @extends foundry.applications.api.ApplicationV2\n *\n * @example\n * // Create a new React-powered Foundry application\n * const app = new FoundryReactApplication({\n * reactApp: MyReactComponent,\n * initialProps: { data: 'example' },\n * window: { title: \"My React App\" },\n * position: { width: 600, height: 400 }\n * });\n *\n * @property {React.Component} reactApp - The React component to be mounted\n * @property {string} template - Path to the Handlebars template for the application shell\n * @property {Object} initialProps - Initial properties passed to the React component\n * @property {string} rootId - ID added to the root element where the React app will be mounted\n */\nexport class ReactApplicationV2 extends ReactApplicationMixin(foundry.applications.api.ApplicationV2) {}\n","/**\n * A React-enabled version of Foundry VTT's ActorSheetV2 class.\n *\n * This class extends the core ActorSheetV2 functionality by applying the ReactApplicationMixin,\n * which enables React component rendering within the actor sheet application.\n *\n * @extends {foundry.applications.sheets.ActorSheetV2}\n * @mixes ReactApplicationMixin\n *\n * @example\n * ```typescript\n * class MyActorSheet extends ReactActorSheetV2 {\n * // Your custom React-enabled actor sheet implementation\n * }\n * ```\n */\nimport ReactApplicationMixin from \"./react-application-mixin\";\n\nexport class ReactActorSheetV2 extends ReactApplicationMixin(foundry.applications.sheets.ActorSheetV2) {}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../lib/context-connector.ts","../lib/util/mount-app.tsx","../lib/react-application-mixin.ts","../lib/react-application-v2.ts","../lib/react-actor-sheet-v2.ts"],"sourcesContent":["/**\n * Event-based bridge for pushing Foundry context updates into React.\n *\n * A `ReactApplicationMixin` instance creates one connector and calls\n * {@link ContextConnector.publishContext} on every render. React components\n * subscribe with {@link ContextConnector.onUpdate} to re-render on changes.\n */\nexport class ContextConnector<T> extends EventTarget {\n static UPDATE = \"contextUpdate\";\n\n /**\n * Maps each subscriber callback to the wrapper actually registered with\n * `addEventListener`, so `off`/`tearDown` can remove it by the original\n * callback reference.\n */\n #wrappers = new Map<(data: T) => void, EventListener>();\n\n publishContext(context: T) {\n this.dispatchEvent(\n new CustomEvent(ContextConnector.UPDATE, { detail: context })\n );\n }\n\n /**\n * Subscribe to an event. Returns a disposer that removes the listener,\n * convenient for React `useEffect` cleanup.\n */\n on(event: string, callback: (data: T) => void): () => void {\n if (!this.#wrappers.has(callback)) {\n const wrapper: EventListener = (e) =>\n callback((e as CustomEvent<T>).detail);\n this.#wrappers.set(callback, wrapper);\n this.addEventListener(event, wrapper);\n }\n return () => this.off(event, callback);\n }\n\n /** Subscribe to context updates. Returns a disposer. */\n onUpdate(callback: (data: T) => void): () => void {\n return this.on(ContextConnector.UPDATE, callback);\n }\n\n /** Remove a listener previously added with {@link on}. */\n off(event: string, callback: (data: T) => void) {\n const wrapper = this.#wrappers.get(callback);\n if (wrapper) {\n this.removeEventListener(event, wrapper);\n this.#wrappers.delete(callback);\n }\n }\n\n /** Remove a context-update listener previously added with {@link onUpdate}. */\n tearDown(callback: (data: T) => void) {\n this.off(ContextConnector.UPDATE, callback);\n }\n}\n","import { createRoot } from \"react-dom/client\";\n\nexport function mountApp({\n App,\n element,\n initialProps = {},\n innerSelector,\n}: {\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n App: React.ComponentType<any>;\n element: Element;\n initialProps?: {};\n innerSelector: string;\n}) {\n const root = createRoot(element);\n root.render(\n <div id={innerSelector}>\n <App {...initialProps} />\n </div>\n );\n}\n","import { ContextConnector } from \"./context-connector\";\nimport { mountApp } from \"./util/mount-app\";\n\n/**\n * A mixin that integrates React components with Foundry VTT's Application class.\n * This mixin enables the use of React applications within Foundry VTT's application framework.\n *\n * @param superclass - The base class to extend, typically a Foundry VTT Application class\n * @returns A class that extends the superclass with React integration capabilities\n *\n * @remarks\n * This mixin provides the following features:\n * - Mounting React components within Foundry VTT applications\n * - Context management through ContextConnector\n * - Automatic cleanup and rendering lifecycle management\n * - Default window options for position and configuration\n *\n * @example\n * ```typescript\n * class MyReactApp extends ReactApplicationMixin(Application) {\n * constructor(options) {\n * super({\n * reactApp: MyReactComponent,\n * initialProps: { data: \"example\" },\n * ...options\n * });\n * }\n * }\n * ```\n */\n\ntype ReactApplicationProps = {\n reactApp: React.ComponentType<any>;\n initialProps?: Record<string, any>;\n};\n\nfunction ReactApplicationMixin(Superclass: any) {\n return class ReactApplication extends Superclass {\n reactApp: React.ComponentType<any>;\n uuid = foundry.utils.randomID(12);\n rootId = `react-app-root-${this.uuid}`;\n innerSelector = `react-application-inner-${this.rootId}`;\n contextConnector: ContextConnector<any>;\n\n static DEFAULT_OPTIONS = {\n position: {\n width: 400,\n height: 300,\n },\n window: {\n title: \"Hello React-powered Foundry applications\",\n resizable: true,\n minimizable: true,\n },\n };\n\n initialProps = {};\n\n constructor({ reactApp, initialProps, ...options }: ReactApplicationProps & any) {\n super(options);\n this.reactApp = reactApp;\n this.contextConnector = new ContextConnector();\n this.initialProps = initialProps || {};\n }\n\n get appIsRendered() {\n return !!document.querySelector(`#${this.innerSelector}`);\n }\n\n async _onRender(context: any, options: any) {\n await super._onRender(context, options);\n const el = this.element.querySelectorAll(`#${this.rootId}`);\n\n if (el && !this.appIsRendered) {\n mountApp({\n App: this.reactApp,\n element: el[0],\n initialProps: context.initialProps,\n innerSelector: this.innerSelector,\n });\n }\n this.contextConnector.publishContext(context);\n }\n\n _replaceHTML(result: HTMLElement, content: HTMLElement) {\n if (!this.appIsRendered) {\n content.appendChild(result);\n }\n }\n\n async _prepareContext(options: any) {\n const context = (await super._prepareContext(options)) as any;\n context.initialProps = this.initialProps;\n return context;\n }\n\n async _renderHTML() {\n const tempEl = document.createElement(\"div\");\n tempEl.id = this.rootId;\n tempEl.innerHTML = `<span>Uh oh, something went wrong</span>`;\n return tempEl;\n }\n };\n}\n\nexport default ReactApplicationMixin;\n","import ReactApplicationMixin from \"./react-application-mixin\";\n\n/**\n * A Foundry VTT Application class that integrates React components with the Foundry application framework.\n * Extends ApplicationV2 to provide seamless React app mounting and rendering capabilities.\n *\n * @class FoundryReactApplication\n * @extends foundry.applications.api.ApplicationV2\n *\n * @example\n * // Create a new React-powered Foundry application\n * const app = new FoundryReactApplication({\n * reactApp: MyReactComponent,\n * initialProps: { data: 'example' },\n * window: { title: \"My React App\" },\n * position: { width: 600, height: 400 }\n * });\n *\n * @property {React.Component} reactApp - The React component to be mounted\n * @property {string} template - Path to the Handlebars template for the application shell\n * @property {Object} initialProps - Initial properties passed to the React component\n * @property {string} rootId - ID added to the root element where the React app will be mounted\n */\nexport class ReactApplicationV2 extends ReactApplicationMixin(foundry.applications.api.ApplicationV2) {}\n","/**\n * A React-enabled version of Foundry VTT's ActorSheetV2 class.\n *\n * This class extends the core ActorSheetV2 functionality by applying the ReactApplicationMixin,\n * which enables React component rendering within the actor sheet application.\n *\n * @extends {foundry.applications.sheets.ActorSheetV2}\n * @mixes ReactApplicationMixin\n *\n * @example\n * ```typescript\n * class MyActorSheet extends ReactActorSheetV2 {\n * // Your custom React-enabled actor sheet implementation\n * }\n * ```\n */\nimport ReactApplicationMixin from \"./react-application-mixin\";\n\nexport class ReactActorSheetV2 extends ReactApplicationMixin(foundry.applications.sheets.ActorSheetV2) {}\n"],"mappings":"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","names":[]}
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
* with a Vite development server, this function injects necessary scripts to enable React Fast Refresh and loads
|
|
6
6
|
* the React application entrypoint.
|
|
7
7
|
*
|
|
8
|
+
* @deprecated Use the `foundry-vtt-react/vite` plugin instead. It derives the refresh preamble and paths
|
|
9
|
+
* from your Vite config (no hand-maintained paths) and serves the dev entry via dev-server middleware, so
|
|
10
|
+
* the `src/main.js` shim that calls `devSetup` is no longer needed. This function will be removed in a future major.
|
|
11
|
+
*
|
|
8
12
|
* @param {string} appId - The application ID used to construct module paths, typically the `id` value in `module.json`.
|
|
9
13
|
* @param {string} entrypoint - The path to the React application entrypoint script relative to the module root. This should match
|
|
10
14
|
* the entrypoint defined in the Vite configuration.
|
package/dist/util/dev-setup.mjs
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
/** Options for the `foundry-vtt-react` Vite plugin. All optional; Foundry-friendly defaults fill in. */
|
|
4
|
+
interface FoundryReactOptions {
|
|
5
|
+
/** Module `id`. Defaults to the `id` in `./module.json`. */
|
|
6
|
+
appId?: string;
|
|
7
|
+
/** App entry / build input, relative to the project root. Default `"src/main.ts"`. */
|
|
8
|
+
entry?: string;
|
|
9
|
+
/** Foundry server to proxy non-bundle requests to. Default `"http://localhost:30000"`. */
|
|
10
|
+
foundryUrl?: string;
|
|
11
|
+
/** Dev server port. Default `30001`. */
|
|
12
|
+
port?: number;
|
|
13
|
+
/** Bundle filename Foundry requests. Defaults to the basename of `esmodules[0]`, else `"main.js"`. */
|
|
14
|
+
manifestEntry?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Read the Fast Refresh preamble template from `@vitejs/plugin-react` (its source of truth).
|
|
18
|
+
* Lazy import, never static: a static one couples us to plugin-react's internal-API version.
|
|
19
|
+
* plugin-react is required for the dev server, so a missing dep throws naturally here. `??` covers
|
|
20
|
+
* both export shapes (named pre-v6, default-export property since).
|
|
21
|
+
*/
|
|
22
|
+
declare function loadPreambleTemplate(): Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Vite plugin wiring a React module into a local Foundry VTT instance: owns the Foundry-specific
|
|
25
|
+
* config (`base`, `root`, `server.proxy`, `build`, react dedupe — only where you haven't set it)
|
|
26
|
+
* and serves a Fast Refresh preamble at the manifest URL so Foundry boots with HMR, no shim file.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* export default defineConfig({ plugins: [react(), foundryReact()] });
|
|
30
|
+
*/
|
|
31
|
+
declare function foundryReact(options?: FoundryReactOptions): Plugin;
|
|
32
|
+
/**
|
|
33
|
+
* Preamble (base-substituted) + a **dynamic** import of the real entry. Dynamic is required: a
|
|
34
|
+
* static `import` hoists above `injectIntoGlobalHook(window)` and breaks Fast Refresh.
|
|
35
|
+
*/
|
|
36
|
+
declare function buildDevModule(base: string, entryUrl: string, preambleTemplate: string): string;
|
|
37
|
+
|
|
38
|
+
export { type FoundryReactOptions, buildDevModule, foundryReact as default, loadPreambleTemplate };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// lib/vite/index.ts
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { basename, resolve } from "path";
|
|
4
|
+
var PLUGIN_NAME = "vite-plugin-foundry-react";
|
|
5
|
+
async function loadPreambleTemplate() {
|
|
6
|
+
const mod = await import("@vitejs/plugin-react");
|
|
7
|
+
return mod.default?.preambleCode ?? mod.preambleCode;
|
|
8
|
+
}
|
|
9
|
+
function readManifest(root) {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(readFileSync(resolve(root, "module.json"), "utf-8"));
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function foundryReact(options = {}) {
|
|
17
|
+
let base = "";
|
|
18
|
+
let entryUrl = "";
|
|
19
|
+
let manifestEntry = "";
|
|
20
|
+
let foundryUrl = "";
|
|
21
|
+
return {
|
|
22
|
+
name: PLUGIN_NAME,
|
|
23
|
+
config(userConfig) {
|
|
24
|
+
const root = userConfig.root ? resolve(process.cwd(), userConfig.root) : process.cwd();
|
|
25
|
+
const manifest = readManifest(process.cwd());
|
|
26
|
+
const appId = options.appId ?? manifest?.id;
|
|
27
|
+
if (!appId) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`[${PLUGIN_NAME}] Could not determine appId. Pass { appId } to the plugin, or run from a directory containing a module.json with an "id" field.`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const entry = options.entry ?? "src/main.ts";
|
|
33
|
+
foundryUrl = options.foundryUrl ?? "http://localhost:30000";
|
|
34
|
+
const port = options.port ?? 30001;
|
|
35
|
+
manifestEntry = options.manifestEntry ?? (manifest?.esmodules?.[0] ? basename(manifest.esmodules[0]) : "main.js");
|
|
36
|
+
base = `/modules/${appId}/dist`;
|
|
37
|
+
entryUrl = `${base}/${basename(entry)}`;
|
|
38
|
+
const proxy = {
|
|
39
|
+
[`^(?!${base})`]: foundryUrl,
|
|
40
|
+
// anything but the dev bundle → Foundry
|
|
41
|
+
"/socket.io": { target: foundryUrl.replace(/^http/, "ws"), ws: true }
|
|
42
|
+
};
|
|
43
|
+
const next = {};
|
|
44
|
+
if (userConfig.base == null) next.base = base;
|
|
45
|
+
if (userConfig.root == null) next.root = "src";
|
|
46
|
+
next.server = {
|
|
47
|
+
port: userConfig.server?.port ?? port,
|
|
48
|
+
proxy: { ...proxy, ...userConfig.server?.proxy }
|
|
49
|
+
};
|
|
50
|
+
next.build = {
|
|
51
|
+
outDir: userConfig.build?.outDir ?? resolve(root, "dist"),
|
|
52
|
+
emptyOutDir: userConfig.build?.emptyOutDir ?? true,
|
|
53
|
+
rollupOptions: {
|
|
54
|
+
input: userConfig.build?.rollupOptions?.input ?? resolve(root, entry),
|
|
55
|
+
output: userConfig.build?.rollupOptions?.output ?? {
|
|
56
|
+
entryFileNames: "[name].js",
|
|
57
|
+
assetFileNames: "[name].[ext]",
|
|
58
|
+
format: "es"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const dedupe = ["react", "react-dom"].filter((d) => !userConfig.resolve?.dedupe?.includes(d));
|
|
63
|
+
if (dedupe.length) next.resolve = { dedupe };
|
|
64
|
+
return next;
|
|
65
|
+
},
|
|
66
|
+
async configureServer(server) {
|
|
67
|
+
const manifestUrl = `${base}/${manifestEntry}`;
|
|
68
|
+
const preambleTemplate = await loadPreambleTemplate();
|
|
69
|
+
const body = buildDevModule(base, entryUrl, preambleTemplate);
|
|
70
|
+
server.middlewares.use((req, res, next) => {
|
|
71
|
+
const url = req.url?.split("?")[0];
|
|
72
|
+
if (url !== manifestUrl) return next();
|
|
73
|
+
res.setHeader("Content-Type", "text/javascript");
|
|
74
|
+
res.end(body);
|
|
75
|
+
});
|
|
76
|
+
server.config.logger.info(
|
|
77
|
+
` ${PLUGIN_NAME} serving dev entry at ${manifestUrl} (proxying to ${foundryUrl})`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function buildDevModule(base, entryUrl, preambleTemplate) {
|
|
83
|
+
const baseWithSlash = base.endsWith("/") ? base : `${base}/`;
|
|
84
|
+
return `${preambleTemplate.replace("__BASE__", baseWithSlash)}
|
|
85
|
+
import(${JSON.stringify(entryUrl)});
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
buildDevModule,
|
|
90
|
+
foundryReact as default,
|
|
91
|
+
loadPreambleTemplate
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../lib/vite/index.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { basename, resolve } from \"node:path\";\nimport type { Plugin, ProxyOptions, UserConfig } from \"vite\";\n\n/** Options for the `foundry-vtt-react` Vite plugin. All optional; Foundry-friendly defaults fill in. */\nexport interface FoundryReactOptions {\n /** Module `id`. Defaults to the `id` in `./module.json`. */\n appId?: string;\n /** App entry / build input, relative to the project root. Default `\"src/main.ts\"`. */\n entry?: string;\n /** Foundry server to proxy non-bundle requests to. Default `\"http://localhost:30000\"`. */\n foundryUrl?: string;\n /** Dev server port. Default `30001`. */\n port?: number;\n /** Bundle filename Foundry requests. Defaults to the basename of `esmodules[0]`, else `\"main.js\"`. */\n manifestEntry?: string;\n}\n\nconst PLUGIN_NAME = \"vite-plugin-foundry-react\";\n\n/**\n * Read the Fast Refresh preamble template from `@vitejs/plugin-react` (its source of truth).\n * Lazy import, never static: a static one couples us to plugin-react's internal-API version.\n * plugin-react is required for the dev server, so a missing dep throws naturally here. `??` covers\n * both export shapes (named pre-v6, default-export property since).\n */\nexport async function loadPreambleTemplate(): Promise<string> {\n const mod: any = await import(\"@vitejs/plugin-react\");\n return mod.default?.preambleCode ?? mod.preambleCode;\n}\n\nfunction readManifest(root: string): { id?: string; esmodules?: string[] } | null {\n try {\n return JSON.parse(readFileSync(resolve(root, \"module.json\"), \"utf-8\"));\n } catch {\n return null;\n }\n}\n\n/**\n * Vite plugin wiring a React module into a local Foundry VTT instance: owns the Foundry-specific\n * config (`base`, `root`, `server.proxy`, `build`, react dedupe — only where you haven't set it)\n * and serves a Fast Refresh preamble at the manifest URL so Foundry boots with HMR, no shim file.\n *\n * @example\n * export default defineConfig({ plugins: [react(), foundryReact()] });\n */\nexport default function foundryReact(options: FoundryReactOptions = {}): Plugin {\n let base = \"\";\n let entryUrl = \"\";\n let manifestEntry = \"\";\n let foundryUrl = \"\";\n\n return {\n name: PLUGIN_NAME,\n\n config(userConfig): UserConfig {\n const root = userConfig.root ? resolve(process.cwd(), userConfig.root) : process.cwd();\n const manifest = readManifest(process.cwd());\n\n const appId = options.appId ?? manifest?.id;\n if (!appId) {\n throw new Error(\n `[${PLUGIN_NAME}] Could not determine appId. Pass { appId } to the plugin, ` +\n `or run from a directory containing a module.json with an \"id\" field.`,\n );\n }\n\n const entry = options.entry ?? \"src/main.ts\";\n foundryUrl = options.foundryUrl ?? \"http://localhost:30000\";\n const port = options.port ?? 30001;\n manifestEntry =\n options.manifestEntry ?? (manifest?.esmodules?.[0] ? basename(manifest.esmodules[0]) : \"main.js\");\n\n base = `/modules/${appId}/dist`;\n entryUrl = `${base}/${basename(entry)}`;\n\n const proxy: Record<string, string | ProxyOptions> = {\n [`^(?!${base})`]: foundryUrl, // anything but the dev bundle → Foundry\n \"/socket.io\": { target: foundryUrl.replace(/^http/, \"ws\"), ws: true },\n };\n\n // Only fill what the user hasn't set, so their config always wins.\n const next: UserConfig = {};\n if (userConfig.base == null) next.base = base;\n if (userConfig.root == null) next.root = \"src\";\n next.server = {\n port: userConfig.server?.port ?? port,\n proxy: { ...proxy, ...userConfig.server?.proxy },\n };\n next.build = {\n outDir: userConfig.build?.outDir ?? resolve(root, \"dist\"),\n emptyOutDir: userConfig.build?.emptyOutDir ?? true,\n rollupOptions: {\n input: userConfig.build?.rollupOptions?.input ?? resolve(root, entry),\n output: userConfig.build?.rollupOptions?.output ?? {\n entryFileNames: \"[name].js\",\n assetFileNames: \"[name].[ext]\",\n format: \"es\",\n },\n },\n };\n\n // Force a single react/react-dom — a linked/git-installed copy otherwise duplicates React\n // (\"Invalid hook call\"). Append only what the user hasn't (Vite concatenates dedupe lists).\n const dedupe = [\"react\", \"react-dom\"].filter((d) => !userConfig.resolve?.dedupe?.includes(d));\n if (dedupe.length) next.resolve = { dedupe };\n\n return next;\n },\n\n async configureServer(server) {\n const manifestUrl = `${base}/${manifestEntry}`;\n const preambleTemplate = await loadPreambleTemplate();\n const body = buildDevModule(base, entryUrl, preambleTemplate);\n\n // Serve the preamble + dynamic entry import at the manifest URL (replaces the dev shim file).\n server.middlewares.use((req, res, next) => {\n const url = req.url?.split(\"?\")[0];\n if (url !== manifestUrl) return next();\n\n res.setHeader(\"Content-Type\", \"text/javascript\");\n res.end(body);\n });\n\n server.config.logger.info(\n ` ${PLUGIN_NAME} serving dev entry at ${manifestUrl} (proxying to ${foundryUrl})`,\n );\n },\n };\n}\n\n/**\n * Preamble (base-substituted) + a **dynamic** import of the real entry. Dynamic is required: a\n * static `import` hoists above `injectIntoGlobalHook(window)` and breaks Fast Refresh.\n */\nexport function buildDevModule(base: string, entryUrl: string, preambleTemplate: string): string {\n // plugin-react's `__BASE__` expects Vite's normalized base (trailing slash).\n const baseWithSlash = base.endsWith(\"/\") ? base : `${base}/`;\n return `${preambleTemplate.replace(\"__BASE__\", baseWithSlash)}\\nimport(${JSON.stringify(entryUrl)});\\n`;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foundry-vtt-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Extensions of various FoundryVTT classes to support React applications.",
|
|
5
5
|
"packageManager": "pnpm@10.30.3",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"./dev-setup": {
|
|
14
14
|
"types": "./dist/util/dev-setup.d.mts",
|
|
15
15
|
"import": "./dist/util/dev-setup.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./vite": {
|
|
18
|
+
"types": "./dist/vite/index.d.mts",
|
|
19
|
+
"import": "./dist/vite/index.mjs"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"files": [
|
|
@@ -21,19 +25,40 @@
|
|
|
21
25
|
"scripts": {
|
|
22
26
|
"build": "tsup",
|
|
23
27
|
"build:watch": "tsup --watch",
|
|
28
|
+
"prepare": "tsup",
|
|
24
29
|
"prepublishOnly": "pnpm build"
|
|
25
30
|
},
|
|
26
31
|
"keywords": [],
|
|
27
|
-
"author": "",
|
|
28
|
-
"license": "
|
|
32
|
+
"author": "Tim Sandberg",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/tasandberg/foundry-vtt-react.git"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/tasandberg/foundry-vtt-react#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/tasandberg/foundry-vtt-react/issues"
|
|
41
|
+
},
|
|
29
42
|
"devDependencies": {
|
|
43
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
30
44
|
"fvtt-types": "^13",
|
|
31
45
|
"tsup": "^8.5.1",
|
|
32
|
-
"typescript": "^5.9.3"
|
|
46
|
+
"typescript": "^5.9.3",
|
|
47
|
+
"vite": "^8.0.16"
|
|
33
48
|
},
|
|
34
49
|
"peerDependencies": {
|
|
50
|
+
"@vitejs/plugin-react": ">=4",
|
|
35
51
|
"react": "^19.2.0",
|
|
36
|
-
"react-dom": "^19.2.0"
|
|
52
|
+
"react-dom": "^19.2.0",
|
|
53
|
+
"vite": ">=5"
|
|
54
|
+
},
|
|
55
|
+
"peerDependenciesMeta": {
|
|
56
|
+
"@vitejs/plugin-react": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
59
|
+
"vite": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
37
62
|
},
|
|
38
63
|
"dependencies": {
|
|
39
64
|
"@types/react": "^19.2.5",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/util/logger.ts","../lib/util/dev-setup.ts"],"sourcesContent":["const logger =\n (namespace: string): ((message: string) => void) =>\n (message: string) => {\n console.log(`%c[foundry-vtt-react][${namespace}]`, \"color: tomato;\", message);\n };\n\nexport default logger;\n","import logger from \"../util/logger.js\";\nconst log = logger(\"dev-setup\");\n\n/**\n * devSetup.js\n *\n * Utility function to set up development environment for React applications in Foundry VTT. Used in conjunction\n * with a Vite development server, this function injects necessary scripts to enable React Fast Refresh and loads\n * the React application entrypoint.\n *\n * @param {string} appId - The application ID used to construct module paths, typically the `id` value in `module.json`.\n * @param {string} entrypoint - The path to the React application entrypoint script relative to the module root. This should match\n * the entrypoint defined in the Vite configuration.\n */\nexport function devSetup(appId: string, entrypoint: string) {\n const refreshScriptId = `foundry-react-refresh-script-${appId}`;\n\n if (document.getElementById(refreshScriptId)) {\n log(\"Script tag already exists, not adding again\");\n return;\n } else {\n log(\"Adding script tag for react refresh\");\n\n const scriptInner = `\n import { injectIntoGlobalHook } from \"/modules/${appId}/dist/@react-refresh\";\n injectIntoGlobalHook(window);\n window.$RefreshReg$ = () => {};\n window.$RefreshSig$ = () => (type) => type;\n `;\n\n const tag = document.createElement(\"script\");\n tag.type = \"module\";\n tag.id = refreshScriptId;\n tag.innerHTML = scriptInner;\n document.head.prepend(tag);\n }\n\n const devEntrypointId = `foundry-react-dev-entrypoint-${appId}`;\n if (document.getElementById(devEntrypointId)) {\n log(\"Dev entrypoint script tag already exists, not adding again\");\n return;\n } else {\n const mainScript = document.createElement(\"script\");\n mainScript.type = \"module\";\n mainScript.src = `/modules/${appId}${entrypoint.startsWith(\"/\") ? \"\" : \"/\"}${entrypoint}`;\n document.body.appendChild(mainScript);\n }\n}\n"],"mappings":";AAAA,IAAM,SACJ,CAAC,cACD,CAAC,YAAoB;AACnB,UAAQ,IAAI,yBAAyB,SAAS,KAAK,kBAAkB,OAAO;AAC9E;AAEF,IAAO,iBAAQ;;;ACLf,IAAM,MAAM,eAAO,WAAW;AAavB,SAAS,SAAS,OAAe,YAAoB;AAC1D,QAAM,kBAAkB,gCAAgC,KAAK;AAE7D,MAAI,SAAS,eAAe,eAAe,GAAG;AAC5C,QAAI,6CAA6C;AACjD;AAAA,EACF,OAAO;AACL,QAAI,qCAAqC;AAEzC,UAAM,cAAc;AAAA,uDAC+B,KAAK;AAAA;AAAA;AAAA;AAAA;AAMxD,UAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,QAAI,OAAO;AACX,QAAI,KAAK;AACT,QAAI,YAAY;AAChB,aAAS,KAAK,QAAQ,GAAG;AAAA,EAC3B;AAEA,QAAM,kBAAkB,gCAAgC,KAAK;AAC7D,MAAI,SAAS,eAAe,eAAe,GAAG;AAC5C,QAAI,4DAA4D;AAChE;AAAA,EACF,OAAO;AACL,UAAM,aAAa,SAAS,cAAc,QAAQ;AAClD,eAAW,OAAO;AAClB,eAAW,MAAM,YAAY,KAAK,GAAG,WAAW,WAAW,GAAG,IAAI,KAAK,GAAG,GAAG,UAAU;AACvF,aAAS,KAAK,YAAY,UAAU;AAAA,EACtC;AACF;","names":[]}
|