wuira 0.0.0-stage → 0.1.1
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 +254 -2
- package/cli/wuira.mjs +67 -0
- package/config/tsconfig.json +25 -0
- package/lib/Console.d.ts +5 -0
- package/lib/Console.lua +20 -0
- package/lib/console-polyfill.d.ts +9 -0
- package/lib/console-polyfill.lua +39 -0
- package/lib/datasources/DataSource.d.ts +17 -0
- package/lib/datasources/DataSource.lua +199 -0
- package/lib/datasources/NativeNumberSource.d.ts +7 -0
- package/lib/datasources/NativeNumberSource.lua +50 -0
- package/lib/datasources/ProgressSource.d.ts +23 -0
- package/lib/datasources/ProgressSource.lua +79 -0
- package/lib/datasources/Resources.d.ts +27 -0
- package/lib/datasources/Resources.lua +102 -0
- package/lib/datasources/index.d.ts +4 -0
- package/lib/datasources/index.lua +18 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.lua +11 -0
- package/lib/platform/forever.d.ts +62 -0
- package/lib/types/Anchor.d.ts +6 -0
- package/lib/types/Anchor.lua +8 -0
- package/lib/types/Color.d.ts +6 -0
- package/lib/types/Color.lua +3 -0
- package/lib/types/Position.d.ts +21 -0
- package/lib/types/Position.lua +3 -0
- package/lib/types/Spacing.d.ts +14 -0
- package/lib/types/Spacing.lua +20 -0
- package/lib/types/Texture.d.ts +11 -0
- package/lib/types/Texture.lua +3 -0
- package/lib/ui/Cell.d.ts +21 -0
- package/lib/ui/Cell.lua +8 -0
- package/lib/ui/FlowLayout.d.ts +18 -0
- package/lib/ui/FlowLayout.lua +8 -0
- package/lib/ui/GridLayout.d.ts +19 -0
- package/lib/ui/GridLayout.lua +8 -0
- package/lib/ui/Layout.d.ts +10 -0
- package/lib/ui/Layout.lua +3 -0
- package/lib/ui/ProgressBar.d.ts +32 -0
- package/lib/ui/ProgressBar.lua +8 -0
- package/lib/ui/decorations/Background.d.ts +10 -0
- package/lib/ui/decorations/Background.lua +76 -0
- package/lib/ui/decorations/Border.d.ts +18 -0
- package/lib/ui/decorations/Border.lua +188 -0
- package/lib/ui/decorations/Decoration.d.ts +13 -0
- package/lib/ui/decorations/Decoration.lua +81 -0
- package/lib/ui/decorations/Decorations.d.ts +8 -0
- package/lib/ui/decorations/Decorations.lua +3 -0
- package/lib/ui/decorations/Glow.d.ts +14 -0
- package/lib/ui/decorations/Glow.lua +63 -0
- package/lib/ui/decorations/backgroundTextures.d.ts +25 -0
- package/lib/ui/decorations/backgroundTextures.lua +10 -0
- package/lib/ui/decorations/borderTextures.d.ts +41 -0
- package/lib/ui/decorations/borderTextures.lua +12 -0
- package/lib/ui/decorations/statusBarTextures.d.ts +12 -0
- package/lib/ui/decorations/statusBarTextures.lua +9 -0
- package/lib/ui/index.d.ts +11 -0
- package/lib/ui/index.lua +35 -0
- package/lib/ui/layout/calculateFlow.d.ts +12 -0
- package/lib/ui/layout/calculateFlow.lua +36 -0
- package/lib/ui/layout/calculateGrid.d.ts +9 -0
- package/lib/ui/layout/calculateGrid.lua +47 -0
- package/lib/ui/layout/measureLayout.d.ts +19 -0
- package/lib/ui/layout/measureLayout.lua +215 -0
- package/lib/ui/mount.d.ts +5 -0
- package/lib/ui/mount.lua +486 -0
- package/lib/ui/primitives/jsx.d.ts +38 -0
- package/lib/ui/primitives/jsx.lua +66 -0
- package/lib/wuira.d.ts +8 -0
- package/lib/wuira.lua +30 -0
- package/package.json +74 -4
- package/templates/addon/README.md +13 -0
- package/templates/addon/addon.toc.in +4 -0
- package/templates/addon/gitignore +3 -0
- package/templates/addon/package.json +11 -0
- package/templates/addon/src/index.tsx +9 -0
- package/templates/addon/tsconfig.json +8 -0
- package/templates/addon/wuira.config.mjs +4 -0
- package/tools/build-library.mjs +15 -0
- package/tools/module-loader.lua +16 -0
- package/tools/wow-modules.mjs +56 -0
- package/tsconfig.json +22 -0
- package/tsconfig.lib.json +8 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Uira contributors
|
|
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,3 +1,255 @@
|
|
|
1
|
-
#
|
|
1
|
+
# wuira
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Build WoW Forever interfaces with TypeScript and JSX.
|
|
4
|
+
|
|
5
|
+
wuira is a UI framework and addon toolchain for developers who want to describe their interface in code, compose reusable components, and keep game data separate from presentation. Write TypeScript, arrange native widgets with JSX, and compile your addon to Lua.
|
|
6
|
+
|
|
7
|
+
## Why wuira?
|
|
8
|
+
|
|
9
|
+
An interface should be easy to understand by reading its source. A health display can be a progress bar inside a layout; a player display can be a composition of bars, labels, and icons rather than a special unit-frame class. wuira provides the foundations for that approach, with TypeScript checking your component props before the addon reaches the game.
|
|
10
|
+
|
|
11
|
+
The snippets below use the lowercase JSX primitives. In a generated project, import `{ UI, wuira }` from `wuira` before mounting a view. Each view example is independent.
|
|
12
|
+
|
|
13
|
+
### JSX composition
|
|
14
|
+
|
|
15
|
+
<!-- Screenshot: a reusable ResourceBar component inside a Cell. -->
|
|
16
|
+
|
|
17
|
+
Describe UI trees with components and reusable functions. No separate XML view files are required.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
function ResourceBar({ current }: { current: number }) {
|
|
21
|
+
return <progress width="inherit" height={24} min={0} max={100} current={current} />;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function App() {
|
|
25
|
+
return (
|
|
26
|
+
<cell width={200} height={40} padding={8}>
|
|
27
|
+
<ResourceBar current={75} />
|
|
28
|
+
</cell>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
wuira.Mount(App);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Native widgets
|
|
36
|
+
|
|
37
|
+
<!-- Screenshot: a native StatusBar with a dark background. -->
|
|
38
|
+
|
|
39
|
+
Cells mount to WoW frames, and progress bars use the game's native StatusBar widget.
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
const view = (
|
|
43
|
+
<progress
|
|
44
|
+
width={200} height={24}
|
|
45
|
+
min={0} max={100} current={65}
|
|
46
|
+
foreground={{ color: { r: 0.1, g: 0.75, b: 0.4 } }}
|
|
47
|
+
background={{ color: { r: 0.08, g: 0.08, b: 0.08 } }}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
wuira.Mount(view);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Layout primitives
|
|
55
|
+
|
|
56
|
+
<!-- Screenshot: two vertically stacked progress bars with a gap. -->
|
|
57
|
+
|
|
58
|
+
Flow and grid layouts arrange children, with spacing and sizing options shared across components.
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
const view = (
|
|
62
|
+
<flow width={200} height={56} direction="vertical" gap={8}>
|
|
63
|
+
<progress height={24} min={0} max={100} current={80} />
|
|
64
|
+
<progress height={24} min={0} max={100} current={45} />
|
|
65
|
+
</flow>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
wuira.Mount(view);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For rows and columns, use `<grid columns={2} gap={8}>` with an explicit width and height.
|
|
72
|
+
|
|
73
|
+
### Data sources
|
|
74
|
+
|
|
75
|
+
<!-- Screenshot: live player health and mana bars. -->
|
|
76
|
+
|
|
77
|
+
Connect health and mana values to progress bars without writing a separate resource-specific container class.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { HP, Mana, Unit } from "wuira/datasources";
|
|
81
|
+
|
|
82
|
+
function App() {
|
|
83
|
+
const hp = new HP(Unit.Player);
|
|
84
|
+
const mana = new Mana(Unit.Player);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<flow width={200} height={52} direction="vertical" gap={4}>
|
|
88
|
+
<progress height={24} source={hp} />
|
|
89
|
+
<progress height={24} source={mana} />
|
|
90
|
+
</flow>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
wuira.Mount(App);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Visual styling
|
|
98
|
+
|
|
99
|
+
<!-- Screenshot: a dark Cell with a thin colored border. -->
|
|
100
|
+
|
|
101
|
+
Configure backgrounds, borders, textures, and glow through reusable decoration objects.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { Background, Border } from "wuira/ui";
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
const view = (
|
|
108
|
+
<cell width={200} height={64} decorations={{
|
|
109
|
+
background: new Background({ color: { r: 0.06, g: 0.08, b: 0.1 } }),
|
|
110
|
+
border: new Border({ thickness: 1, color: { r: 0.3, g: 0.8, b: 0.7 } }),
|
|
111
|
+
}} />
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
wuira.Mount(view);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Addon tooling
|
|
118
|
+
|
|
119
|
+
<!-- Screenshot: creating and building an addon in the terminal. -->
|
|
120
|
+
|
|
121
|
+
Create a project, compile TypeScriptToLua, and generate the TOC and module-loading files through one CLI.
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
npx wuira@latest create MyAddon
|
|
125
|
+
cd MyAddon
|
|
126
|
+
npm install
|
|
127
|
+
npm run build
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Readable Lua output
|
|
131
|
+
|
|
132
|
+
<!-- Screenshot: source files alongside their emitted Lua modules. -->
|
|
133
|
+
|
|
134
|
+
Emitted modules remain split across files following the source structure.
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
src/index.tsx → dist/index.lua
|
|
138
|
+
src/components/Panel.tsx → dist/components/Panel.lua
|
|
139
|
+
wuira/ui dependency → dist/lua_modules/wuira/lib/ui/index.lua
|
|
140
|
+
|
|
141
|
+
MyAddon.toc loads the module registry, emitted files, and startup entry.
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Use wuira when you want to build and distribute an addon UI in TypeScript, share visual components, and let the toolchain handle Lua module loading. Node.js runs during development; the game loads the generated Lua files without a JavaScript runtime.
|
|
145
|
+
|
|
146
|
+
## Scope and current status
|
|
147
|
+
|
|
148
|
+
wuira is an early framework for addon authors. It is not a complete UI replacement, an in-game visual editor, or a WeakAuras replacement. JSX describes widgets; there is no React runtime or automatic reactive rendering layer.
|
|
149
|
+
|
|
150
|
+
The current core includes Cells, flow and grid layouts, progress bars, decorations, health and mana sources, a local console polyfill, and project tooling. A complete WoW API surface, secure action and click-cast abstractions, nameplate adapters, and preset management are not implemented yet. Combat restrictions and secret-value rules still apply; framework abstractions cannot bypass them.
|
|
151
|
+
|
|
152
|
+
The target client is WoW Forever. Compatibility with Retail or other Classic clients is not promised without verification. Expect API changes while the framework takes shape.
|
|
153
|
+
|
|
154
|
+
## Getting started
|
|
155
|
+
|
|
156
|
+
The framework, npm package, CLI command, and public API are named `wuira`. Future extension packages can use the `@wuira/` organization scope once that organization is registered.
|
|
157
|
+
|
|
158
|
+
Available on npm as [`wuira`](https://www.npmjs.com/package/wuira):
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
npx wuira create MyAddon
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
A TypeScript and JSX UI framework for WoW Forever. TypeScriptToLua emits separate Lua files following the source directory structure. No Rollup or single-file Lua bundle is required.
|
|
165
|
+
|
|
166
|
+
## CLI
|
|
167
|
+
|
|
168
|
+
Create an addon with Node.js 22.13 or newer:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
npx wuira@latest create MyAddon
|
|
172
|
+
cd MyAddon
|
|
173
|
+
npm install
|
|
174
|
+
npm run build
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Alternatively, install globally with `npm install -g wuira`, then use `wuira create MyAddon`. To test unpublished changes, run `npm install` and `npm link` in this repository, or invoke `node /path/to/wuira/cli/wuira.mjs create MyAddon` directly. Templates reflect the installed CLI version; repository changes become available through npm after the next release.
|
|
178
|
+
|
|
179
|
+
`create` refuses to overwrite an existing directory. It generates TypeScriptToLua configuration, addon metadata, a module loader, an MIT license, and this minimal `src/index.tsx`:
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
import "wuira/console-polyfill";
|
|
183
|
+
import { UI, wuira } from "wuira";
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
function App() {
|
|
187
|
+
console.log("MyAddon loaded");
|
|
188
|
+
return <></>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
wuira.Mount(App);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
An empty fragment creates no native frame. Only empty fragments are currently supported; use a layout for multiple visual elements.
|
|
195
|
+
|
|
196
|
+
`console.log/info/warn/error` use a global WoW console polyfill. Output goes to the local chat window, never to other players. The addon uses no Node.js runtime or stdin/stdout. `console.error` does not throw. Arguments are formatted with Lua `tostring`; objects are not deeply inspected. The CLI uses its own Node.js terminal console.
|
|
197
|
+
|
|
198
|
+
## Build and distribution
|
|
199
|
+
|
|
200
|
+
`wuira build` transpiles through `tsconfig.json`. It writes a standalone addon directory containing individual Lua modules, a loader, a startup file, and `<addonName>.toc`. The default output directory is `dist/`. The addon name lives in `wuira.config.mjs`; Interface version and other metadata live in `addon.toc.in`. The default Interface version, 16001, targets the Forever playground.
|
|
201
|
+
|
|
202
|
+
Copy the contents of `dist/` to `Interface/AddOns/<addonName>/`, or configure the build to output there directly. No Node.js runtime or separate wuira addon is required in game. Module registries are isolated by addon name.
|
|
203
|
+
|
|
204
|
+
Framework code and generated `.d.ts` declarations come from the installed npm package. Import UI components from `wuira/ui`, data sources from `wuira/datasources`, and the mounting API from `wuira`. No framework source files or build scripts are copied into addon projects. This repository contains only the framework and CLI; addon entry points and TOC metadata are generated in consumer projects.
|
|
205
|
+
|
|
206
|
+
Project templates live in `templates/addon/`. Generated projects depend on `wuira` and extend `wuira/config/tsconfig.json`. Shared settings and the TSTL module plugin stay in the installed package; build scripts are not copied into projects. Project-local `rootDir`, `outDir`, and source includes remain in the generated `tsconfig.json`.
|
|
207
|
+
|
|
208
|
+
`npm run build` resolves the locally installed `wuira` command automatically. Use `npx wuira build` when invoking the CLI directly.
|
|
209
|
+
|
|
210
|
+
Configure the addon output in `wuira.config.mjs`:
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
export default {
|
|
214
|
+
name: "MyAddon",
|
|
215
|
+
outDir: "./dist",
|
|
216
|
+
// Or: outDir: "G:/Games/WoW/Interface/AddOns/MyAddon",
|
|
217
|
+
};
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Relative output paths resolve from the project directory. Absolute paths can target the game's addon directory, keeping the development project elsewhere. Builds compile into a temporary staging directory before copying successful output to the destination. Existing files with matching names are overwritten; unrelated files are retained. The emitted TOC uses paths relative to the output folder, so the entire folder can be moved independently.
|
|
221
|
+
|
|
222
|
+
The CLI, configuration, and build plugin use ESM (`.mjs`). Only Lua and TOC files are needed in game. `npm run build` in the framework repository generates distributable Lua modules and TypeScript declarations in `lib/`; addon builds include the framework modules they import.
|
|
223
|
+
|
|
224
|
+
## Development
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
npm ci
|
|
228
|
+
npm run build
|
|
229
|
+
npm run check
|
|
230
|
+
npm test
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`.github/workflows/ci.yml` runs type checks, builds, tests, and a package dry run on pushes and pull requests, on both Linux and Windows with Node.js 22. CI does not publish packages and requires no npm token.
|
|
234
|
+
|
|
235
|
+
Cell is the basic visual box. FlowLayout and GridLayout arrange children. ProgressBar uses a native StatusBar, while data sources connect values to presentation. Behavior from other WoW versions must be verified against the Forever API before use.
|
|
236
|
+
|
|
237
|
+
Licensed under MIT; see [LICENSE](LICENSE).
|
|
238
|
+
|
|
239
|
+
## Releases
|
|
240
|
+
|
|
241
|
+
The first version is published. To publish the next version locally, validate the project, increment the patch version, and publish:
|
|
242
|
+
|
|
243
|
+
```sh
|
|
244
|
+
npm run check
|
|
245
|
+
npm run build
|
|
246
|
+
npm test
|
|
247
|
+
npm version patch --no-git-tag-version
|
|
248
|
+
npm publish --access public
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Publishing requires npm authentication and 2FA, or a granular access token with write access and Bypass 2FA enabled. Commit both `package.json` and `package-lock.json` after changing the version. npm versions cannot be published twice.
|
|
252
|
+
|
|
253
|
+
For future automated releases, every pushed Git tag triggers `.github/workflows/publish.yml`. It checks types, builds, tests, and publishes to npm. Tags must match `package.json` version, optionally prefixed with `v`. Do not push a release tag for a version already published locally; the workflow would attempt to publish it again.
|
|
254
|
+
|
|
255
|
+
To enable automated publishing, configure the GitHub repository secret `NPM_TOKEN` with an npm granular access token authorized to publish `wuira` and bypass 2FA. Never commit the token. GitHub Actions must be enabled. Local setup does not publish a package or push a tag.
|
package/cli/wuira.mjs
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const root = path.resolve(import.meta.dirname, "..");
|
|
10
|
+
|
|
11
|
+
export function create(name, cwd = process.cwd()) {
|
|
12
|
+
if (!name || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(name)) throw new Error("Use an addon name containing letters, numbers, underscores or hyphens, starting with a letter.");
|
|
13
|
+
const target = path.join(cwd, name);
|
|
14
|
+
if (fs.existsSync(target)) throw new Error(`Destination already exists: ${target}`);
|
|
15
|
+
fs.mkdirSync(target, { recursive: true });
|
|
16
|
+
const version = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")).version;
|
|
17
|
+
const variables = { addonName: name, packageName: name.toLowerCase(), frameworkVersion: version };
|
|
18
|
+
const template = path.join(root, "templates/addon");
|
|
19
|
+
for (const entry of fs.readdirSync(template, { recursive: true, withFileTypes: true })) {
|
|
20
|
+
if (!entry.isFile()) continue;
|
|
21
|
+
const source = path.join(entry.parentPath, entry.name);
|
|
22
|
+
const relative = path.relative(template, source);
|
|
23
|
+
const destination = path.join(target, relative === "gitignore" ? ".gitignore" : relative);
|
|
24
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
25
|
+
const content = fs.readFileSync(source, "utf8").replace(/\{\{(\w+)\}\}/g, (_, key) => {
|
|
26
|
+
if (!(key in variables)) throw new Error(`Unknown template variable: ${key}`);
|
|
27
|
+
return variables[key];
|
|
28
|
+
});
|
|
29
|
+
fs.writeFileSync(destination, content);
|
|
30
|
+
}
|
|
31
|
+
fs.copyFileSync(path.join(root, "LICENSE"), path.join(target, "LICENSE"));
|
|
32
|
+
return target;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function build(cwd = process.cwd()) {
|
|
36
|
+
if (!fs.existsSync(path.join(cwd, "tsconfig.json"))) throw new Error("Run wuira build inside an addon project.");
|
|
37
|
+
const configFile = path.join(cwd, "wuira.config.mjs");
|
|
38
|
+
const config = fs.existsSync(configFile) ? (await import(pathToFileURL(configFile).href + `?updated=${fs.statSync(configFile).mtimeMs}`)).default : {};
|
|
39
|
+
if (config === null || typeof config !== "object") throw new Error("wuira.config.mjs must export a configuration object.");
|
|
40
|
+
if (config.outDir !== undefined && (typeof config.outDir !== "string" || config.outDir.trim() === "")) throw new Error("outDir must be a nonempty path.");
|
|
41
|
+
const output = path.resolve(cwd, config.outDir ?? "dist");
|
|
42
|
+
if (output === path.parse(output).root || output === path.resolve(cwd)) throw new Error("Choose a dedicated addon output directory.");
|
|
43
|
+
const stage = fs.mkdtempSync(path.join(os.tmpdir(), "wuira-build-"));
|
|
44
|
+
const compiler = require.resolve("typescript-to-lua/dist/tstl.js", { paths: [cwd, root] });
|
|
45
|
+
try {
|
|
46
|
+
const result = spawnSync(process.execPath, [compiler, "-p", "tsconfig.json", "--outDir", stage], {
|
|
47
|
+
cwd, stdio: "inherit", env: { ...process.env, WUIRA_STANDALONE: "1", ...(config.name ? { WUIRA_ADDON_NAME: config.name } : {}) },
|
|
48
|
+
});
|
|
49
|
+
if (result.error) throw result.error;
|
|
50
|
+
if (result.status !== 0) throw new Error("Addon build failed.");
|
|
51
|
+
fs.mkdirSync(output, { recursive: true });
|
|
52
|
+
fs.cpSync(stage, output, { recursive: true });
|
|
53
|
+
console.log(`Built addon: ${output}`);
|
|
54
|
+
} finally {
|
|
55
|
+
fs.rmSync(stage, { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
60
|
+
try {
|
|
61
|
+
const [command, name] = process.argv.slice(2);
|
|
62
|
+
if (command === "create") console.log(`Created ${create(name)}\nNext: cd ${name}, npm install, wuira build`);
|
|
63
|
+
else if (command === "build") await build();
|
|
64
|
+
else if (!command || command === "--help") console.log("wuira create <addonName>\nwuira build");
|
|
65
|
+
else throw new Error(`Unknown command: ${command}`);
|
|
66
|
+
} catch (error) { console.error(error.message); process.exitCode = 1; }
|
|
67
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/TypeScriptToLua/TypeScriptToLua/master/tsconfig-schema.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"lib": [
|
|
6
|
+
"ESNext"
|
|
7
|
+
],
|
|
8
|
+
"types": [],
|
|
9
|
+
"jsx": "react",
|
|
10
|
+
"jsxFactory": "UI.createElement",
|
|
11
|
+
"jsxFragmentFactory": "UI.Fragment",
|
|
12
|
+
"strict": true,
|
|
13
|
+
"noEmitOnError": true
|
|
14
|
+
},
|
|
15
|
+
"tstl": {
|
|
16
|
+
"luaTarget": "universal",
|
|
17
|
+
"luaLibImport": "inline",
|
|
18
|
+
"noImplicitGlobalVariables": true,
|
|
19
|
+
"luaPlugins": [
|
|
20
|
+
{
|
|
21
|
+
"name": "wuira/tools/wow-modules.mjs"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
package/lib/Console.d.ts
ADDED
package/lib/Console.lua
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
|
2
|
+
-- Lua Library inline imports
|
|
3
|
+
local function __TS__Class(self)
|
|
4
|
+
local c = {prototype = {}}
|
|
5
|
+
c.prototype.__index = c.prototype
|
|
6
|
+
c.prototype.constructor = c
|
|
7
|
+
return c
|
|
8
|
+
end
|
|
9
|
+
-- End of Lua Library inline imports
|
|
10
|
+
local ____exports = {}
|
|
11
|
+
--- Local client output only; never sends chat messages to other players.
|
|
12
|
+
____exports.Console = __TS__Class()
|
|
13
|
+
local Console = ____exports.Console
|
|
14
|
+
Console.name = "Console"
|
|
15
|
+
function Console.prototype.____constructor(self)
|
|
16
|
+
end
|
|
17
|
+
function Console.Echo(self, message)
|
|
18
|
+
print("|cff60d9c5[wuira]|r " .. message)
|
|
19
|
+
end
|
|
20
|
+
return ____exports
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
|
2
|
+
-- Lua Library inline imports
|
|
3
|
+
local function __TS__ArrayMap(self, callbackfn, thisArg)
|
|
4
|
+
local result = {}
|
|
5
|
+
for i = 1, #self do
|
|
6
|
+
result[i] = callbackfn(thisArg, self[i], i - 1, self)
|
|
7
|
+
end
|
|
8
|
+
return result
|
|
9
|
+
end
|
|
10
|
+
-- End of Lua Library inline imports
|
|
11
|
+
local ____exports = {}
|
|
12
|
+
local function write(self, color, values)
|
|
13
|
+
print((color .. table.concat(
|
|
14
|
+
__TS__ArrayMap(
|
|
15
|
+
values,
|
|
16
|
+
function(____, value) return tostring(value) end
|
|
17
|
+
),
|
|
18
|
+
" "
|
|
19
|
+
)) .. "|r")
|
|
20
|
+
end
|
|
21
|
+
_G.console = {
|
|
22
|
+
log = function(...)
|
|
23
|
+
local values = {...}
|
|
24
|
+
write(nil, "|cffffffff", values)
|
|
25
|
+
end,
|
|
26
|
+
info = function(...)
|
|
27
|
+
local values = {...}
|
|
28
|
+
write(nil, "|cff60d9c5", values)
|
|
29
|
+
end,
|
|
30
|
+
warn = function(...)
|
|
31
|
+
local values = {...}
|
|
32
|
+
write(nil, "|cffffcc00", values)
|
|
33
|
+
end,
|
|
34
|
+
error = function(...)
|
|
35
|
+
local values = {...}
|
|
36
|
+
write(nil, "|cffff5555", values)
|
|
37
|
+
end
|
|
38
|
+
}
|
|
39
|
+
return ____exports
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** A value-producing source. Secrecy describes data, not native frame protection. */
|
|
2
|
+
export declare abstract class DataSource<T> {
|
|
3
|
+
private value;
|
|
4
|
+
private secret;
|
|
5
|
+
protected constructor(value: T, secret?: boolean);
|
|
6
|
+
/** Checked when consumed, rather than inferred from combat state. */
|
|
7
|
+
IsSecret(): boolean;
|
|
8
|
+
get Value(): T;
|
|
9
|
+
Compute<TResult>(compute: (value: T) => TResult): TResult;
|
|
10
|
+
protected GetValue(): T;
|
|
11
|
+
private IsValueSecret;
|
|
12
|
+
/** Replace the value and its explicit restriction together, clearing stale flags. */
|
|
13
|
+
protected SetValue(value: T, secret?: boolean): void;
|
|
14
|
+
/** Aggregate sources override this to check known fields without comparing them. */
|
|
15
|
+
protected HasSecretFields(_value: T): boolean;
|
|
16
|
+
private AssertReadable;
|
|
17
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
|
2
|
+
-- Lua Library inline imports
|
|
3
|
+
local function __TS__Class(self)
|
|
4
|
+
local c = {prototype = {}}
|
|
5
|
+
c.prototype.__index = c.prototype
|
|
6
|
+
c.prototype.constructor = c
|
|
7
|
+
return c
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
local function __TS__CloneDescriptor(____bindingPattern0)
|
|
11
|
+
local value
|
|
12
|
+
local writable
|
|
13
|
+
local set
|
|
14
|
+
local get
|
|
15
|
+
local configurable
|
|
16
|
+
local enumerable
|
|
17
|
+
enumerable = ____bindingPattern0.enumerable
|
|
18
|
+
configurable = ____bindingPattern0.configurable
|
|
19
|
+
get = ____bindingPattern0.get
|
|
20
|
+
set = ____bindingPattern0.set
|
|
21
|
+
writable = ____bindingPattern0.writable
|
|
22
|
+
value = ____bindingPattern0.value
|
|
23
|
+
local descriptor = {enumerable = enumerable == true, configurable = configurable == true}
|
|
24
|
+
local hasGetterOrSetter = get ~= nil or set ~= nil
|
|
25
|
+
local hasValueOrWritableAttribute = writable ~= nil or value ~= nil
|
|
26
|
+
if hasGetterOrSetter and hasValueOrWritableAttribute then
|
|
27
|
+
error("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute.", 0)
|
|
28
|
+
end
|
|
29
|
+
if get or set then
|
|
30
|
+
descriptor.get = get
|
|
31
|
+
descriptor.set = set
|
|
32
|
+
else
|
|
33
|
+
descriptor.value = value
|
|
34
|
+
descriptor.writable = writable == true
|
|
35
|
+
end
|
|
36
|
+
return descriptor
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
local __TS__DescriptorGet
|
|
40
|
+
do
|
|
41
|
+
local getmetatable = _G.getmetatable
|
|
42
|
+
local ____rawget = _G.rawget
|
|
43
|
+
function __TS__DescriptorGet(self, metatable, key)
|
|
44
|
+
while metatable do
|
|
45
|
+
local rawResult = ____rawget(metatable, key)
|
|
46
|
+
if rawResult ~= nil then
|
|
47
|
+
return rawResult
|
|
48
|
+
end
|
|
49
|
+
local descriptors = ____rawget(metatable, "_descriptors")
|
|
50
|
+
if descriptors then
|
|
51
|
+
local descriptor = descriptors[key]
|
|
52
|
+
if descriptor ~= nil then
|
|
53
|
+
if descriptor.get then
|
|
54
|
+
return descriptor.get(self)
|
|
55
|
+
end
|
|
56
|
+
return descriptor.value
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
metatable = getmetatable(metatable)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
local __TS__DescriptorSet
|
|
65
|
+
do
|
|
66
|
+
local getmetatable = _G.getmetatable
|
|
67
|
+
local ____rawget = _G.rawget
|
|
68
|
+
local rawset = _G.rawset
|
|
69
|
+
function __TS__DescriptorSet(self, metatable, key, value)
|
|
70
|
+
while metatable do
|
|
71
|
+
local descriptors = ____rawget(metatable, "_descriptors")
|
|
72
|
+
if descriptors then
|
|
73
|
+
local descriptor = descriptors[key]
|
|
74
|
+
if descriptor ~= nil then
|
|
75
|
+
if descriptor.set then
|
|
76
|
+
descriptor.set(self, value)
|
|
77
|
+
else
|
|
78
|
+
if descriptor.writable == false then
|
|
79
|
+
error(
|
|
80
|
+
((("Cannot assign to read only property '" .. key) .. "' of object '") .. tostring(self)) .. "'",
|
|
81
|
+
0
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
descriptor.value = value
|
|
85
|
+
end
|
|
86
|
+
return
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
metatable = getmetatable(metatable)
|
|
90
|
+
end
|
|
91
|
+
rawset(self, key, value)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
local __TS__SetDescriptor
|
|
96
|
+
do
|
|
97
|
+
local getmetatable = _G.getmetatable
|
|
98
|
+
local function descriptorIndex(self, key)
|
|
99
|
+
return __TS__DescriptorGet(
|
|
100
|
+
self,
|
|
101
|
+
getmetatable(self),
|
|
102
|
+
key
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
local function descriptorNewIndex(self, key, value)
|
|
106
|
+
return __TS__DescriptorSet(
|
|
107
|
+
self,
|
|
108
|
+
getmetatable(self),
|
|
109
|
+
key,
|
|
110
|
+
value
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
function __TS__SetDescriptor(target, key, desc, isPrototype)
|
|
114
|
+
if isPrototype == nil then
|
|
115
|
+
isPrototype = false
|
|
116
|
+
end
|
|
117
|
+
local ____isPrototype_0
|
|
118
|
+
if isPrototype then
|
|
119
|
+
____isPrototype_0 = target
|
|
120
|
+
else
|
|
121
|
+
____isPrototype_0 = getmetatable(target)
|
|
122
|
+
end
|
|
123
|
+
local metatable = ____isPrototype_0
|
|
124
|
+
if not metatable then
|
|
125
|
+
metatable = {}
|
|
126
|
+
setmetatable(target, metatable)
|
|
127
|
+
end
|
|
128
|
+
if not isPrototype and not rawget(metatable, "_isOwnDescriptorMetatable") then
|
|
129
|
+
local instanceMetatable = {}
|
|
130
|
+
instanceMetatable._isOwnDescriptorMetatable = true
|
|
131
|
+
setmetatable(instanceMetatable, metatable)
|
|
132
|
+
setmetatable(target, instanceMetatable)
|
|
133
|
+
metatable = instanceMetatable
|
|
134
|
+
end
|
|
135
|
+
local value = rawget(target, key)
|
|
136
|
+
if value ~= nil then
|
|
137
|
+
rawset(target, key, nil)
|
|
138
|
+
end
|
|
139
|
+
if not rawget(metatable, "_descriptors") then
|
|
140
|
+
metatable._descriptors = {}
|
|
141
|
+
end
|
|
142
|
+
metatable._descriptors[key] = __TS__CloneDescriptor(desc)
|
|
143
|
+
metatable.__index = descriptorIndex
|
|
144
|
+
metatable.__newindex = descriptorNewIndex
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
-- End of Lua Library inline imports
|
|
148
|
+
local ____exports = {}
|
|
149
|
+
--- A value-producing source. Secrecy describes data, not native frame protection.
|
|
150
|
+
____exports.DataSource = __TS__Class()
|
|
151
|
+
local DataSource = ____exports.DataSource
|
|
152
|
+
DataSource.name = "DataSource"
|
|
153
|
+
function DataSource.prototype.____constructor(self, value, secret)
|
|
154
|
+
if secret == nil then
|
|
155
|
+
secret = false
|
|
156
|
+
end
|
|
157
|
+
self.value = value
|
|
158
|
+
self.secret = secret
|
|
159
|
+
end
|
|
160
|
+
function DataSource.prototype.IsSecret(self)
|
|
161
|
+
return self:IsValueSecret(self:GetValue())
|
|
162
|
+
end
|
|
163
|
+
function DataSource.prototype.Compute(self, compute)
|
|
164
|
+
local value = self:GetValue()
|
|
165
|
+
self:AssertReadable(value)
|
|
166
|
+
return compute(nil, value)
|
|
167
|
+
end
|
|
168
|
+
function DataSource.prototype.GetValue(self)
|
|
169
|
+
return self.value
|
|
170
|
+
end
|
|
171
|
+
function DataSource.prototype.IsValueSecret(self, value)
|
|
172
|
+
return issecretvalue(value) or self.secret or self:HasSecretFields(value)
|
|
173
|
+
end
|
|
174
|
+
function DataSource.prototype.SetValue(self, value, secret)
|
|
175
|
+
if secret == nil then
|
|
176
|
+
secret = false
|
|
177
|
+
end
|
|
178
|
+
self.value = value
|
|
179
|
+
self.secret = secret
|
|
180
|
+
end
|
|
181
|
+
function DataSource.prototype.HasSecretFields(self, _value)
|
|
182
|
+
return false
|
|
183
|
+
end
|
|
184
|
+
function DataSource.prototype.AssertReadable(self, value)
|
|
185
|
+
if self:IsValueSecret(value) then
|
|
186
|
+
error("DataSource: secret data cannot be read or used in computations")
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
__TS__SetDescriptor(
|
|
190
|
+
DataSource.prototype,
|
|
191
|
+
"Value",
|
|
192
|
+
{get = function(self)
|
|
193
|
+
local value = self:GetValue()
|
|
194
|
+
self:AssertReadable(value)
|
|
195
|
+
return value
|
|
196
|
+
end},
|
|
197
|
+
true
|
|
198
|
+
)
|
|
199
|
+
return ____exports
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DataSource } from "./DataSource";
|
|
2
|
+
/** Read-only API value; no cached assignment or game-state mutation. */
|
|
3
|
+
export declare class NativeNumberSource extends DataSource<number> {
|
|
4
|
+
private readonly read;
|
|
5
|
+
constructor(read: () => number);
|
|
6
|
+
protected GetValue(): number;
|
|
7
|
+
}
|