wgsl-edit 0.0.24 → 0.0.26
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/README.md +49 -9
- package/dist/SaveEndpoint.d.mts +5 -0
- package/dist/SaveEndpoint.mjs +5 -0
- package/dist/SaveMiddleware.d.mts +9 -0
- package/dist/SaveMiddleware.mjs +49 -0
- package/dist/{WgslEdit-ByXfb3R9.js → WgslEdit-CNK80480.js} +252 -118
- package/dist/WgslEdit.d.ts +45 -1
- package/dist/WgslEdit.js +1 -1
- package/dist/autosave.d.mts +11 -0
- package/dist/autosave.mjs +18 -0
- package/dist/index.js +1 -1
- package/dist/jsx-preact.d.ts +12 -0
- package/dist/jsx-preact.js +0 -0
- package/dist/wgsl-edit.js +313 -522
- package/package.json +24 -6
- package/src/SaveEndpoint.ts +2 -0
- package/src/SaveMiddleware.ts +71 -0
- package/src/WgslEdit.ts +288 -157
- package/src/autosave.ts +24 -0
- package/src/jsx-preact.ts +15 -0
- package/src/test/Autosave.e2e.ts +100 -0
- package/src/test/E2eUtil.ts +6 -0
- package/src/test/Undo.e2e.ts +91 -0
- package/src/test/WgslEdit.e2e.ts +1 -4
package/README.md
CHANGED
|
@@ -10,7 +10,8 @@ Web component for editing WESL/WGSL with CodeMirror 6.
|
|
|
10
10
|
<wgsl-edit></wgsl-edit>
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Features syntax highlighting, linting, multi-file tabs, and light/dark themes
|
|
13
|
+
Features syntax highlighting, linting, multi-file tabs, and light/dark themes
|
|
14
|
+
out of the box.
|
|
14
15
|
|
|
15
16
|
### Inline source
|
|
16
17
|
|
|
@@ -74,19 +75,24 @@ editor.project = { // load a full project
|
|
|
74
75
|
| `line-numbers` | `true` `false` | `false` | Show line numbers |
|
|
75
76
|
| `fetch-libs` | `true` `false` | `true` | Auto-fetch missing libraries from npm |
|
|
76
77
|
| `shader-root` | string | - | Root path for shader imports |
|
|
78
|
+
| `autosave` | `true` `false` | `false` | Persist edits to disk via dev server (use `wgsl-edit/autosave` plugin) |
|
|
77
79
|
|
|
78
80
|
### Properties
|
|
79
81
|
|
|
80
82
|
- `source: string` - Get/set active file content
|
|
81
|
-
- `conditions: Record<string, boolean>` - Get/set conditions for conditional
|
|
82
|
-
|
|
83
|
+
- `conditions: Record<string, boolean>` - Get/set conditions for conditional
|
|
84
|
+
compilation (`@if`/`@elif`/`@else`)
|
|
85
|
+
- `project: WeslProject` - Get/set full project (weslSrc, conditions, constants,
|
|
86
|
+
packageName, libs)
|
|
83
87
|
- `activeFile: string` - Get/set active file name
|
|
84
88
|
- `fileNames: string[]` - List all file names
|
|
85
|
-
- `theme`, `tabs`, `lint`, `lineNumbers`, `readonly`, `shaderRoot`, `fetchLibs`
|
|
89
|
+
- `theme`, `tabs`, `lint`, `lineNumbers`, `readonly`, `shaderRoot`, `fetchLibs`
|
|
90
|
+
- Mirror attributes
|
|
86
91
|
|
|
87
92
|
### Methods
|
|
88
93
|
|
|
89
|
-
- `link(options?): Promise<string>` - Compile WESL sources into WGSL, returns
|
|
94
|
+
- `link(options?): Promise<string>` - Compile WESL sources into WGSL, returns
|
|
95
|
+
the linked output
|
|
90
96
|
- `addFile(name, content?)` - Add a new file
|
|
91
97
|
- `removeFile(name)` - Remove a file
|
|
92
98
|
- `renameFile(oldName, newName)` - Rename a file
|
|
@@ -98,8 +104,8 @@ editor.project = { // load a full project
|
|
|
98
104
|
|
|
99
105
|
## Using with wesl-plugin
|
|
100
106
|
|
|
101
|
-
For full project support (libraries, conditional compilation, constants),
|
|
102
|
-
|
|
107
|
+
For full project support (libraries, conditional compilation, constants), use
|
|
108
|
+
[wesl-plugin](https://github.com/webgpu-tools/wesl-js/tree/main/packages/wesl-plugin)
|
|
103
109
|
to assemble shaders at build time and pass them to the editor via `project`.
|
|
104
110
|
|
|
105
111
|
```typescript
|
|
@@ -122,8 +128,42 @@ editor.project = {
|
|
|
122
128
|
};
|
|
123
129
|
```
|
|
124
130
|
|
|
125
|
-
The `?link` import provides `weslSrc`, `libs`, `rootModuleName`, and
|
|
126
|
-
The editor's linter uses these to validate imports, conditions,
|
|
131
|
+
The `?link` import provides `weslSrc`, `libs`, `rootModuleName`, and
|
|
132
|
+
`packageName`. The editor's linter uses these to validate imports, conditions,
|
|
133
|
+
and constants as you type.
|
|
134
|
+
|
|
135
|
+
## Autosave (dev mode)
|
|
136
|
+
|
|
137
|
+
When using wesl-plugin, edits can be persisted back to the source file on disk
|
|
138
|
+
during Vite development. Add the `wgslEditAutosave()` plugin alongside
|
|
139
|
+
wesl-plugin and set `autosave` on the editor:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// vite.config.ts
|
|
143
|
+
import wgslEditAutosave from "wgsl-edit/autosave";
|
|
144
|
+
import { linkBuildExtension } from "wesl-plugin";
|
|
145
|
+
import viteWesl from "wesl-plugin/vite";
|
|
146
|
+
|
|
147
|
+
export default {
|
|
148
|
+
plugins: [
|
|
149
|
+
viteWesl({ extensions: [linkBuildExtension] }),
|
|
150
|
+
wgslEditAutosave(),
|
|
151
|
+
],
|
|
152
|
+
};
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// app.ts
|
|
157
|
+
import shaderConfig from "./shader.wesl?link";
|
|
158
|
+
|
|
159
|
+
const editor = document.querySelector("wgsl-edit");
|
|
160
|
+
editor.project = shaderConfig;
|
|
161
|
+
editor.autosave = true;
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`shader-root` is picked up automatically from the `?link` import, so edits land
|
|
165
|
+
in the right file on disk. Production builds never include the middleware, so
|
|
166
|
+
the same code is safe to ship.
|
|
127
167
|
|
|
128
168
|
## Styling
|
|
129
169
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
|
|
3
|
+
//#region src/SaveMiddleware.d.ts
|
|
4
|
+
/** Paths recently written by autosave; consumed by hotUpdate to suppress reloads. */
|
|
5
|
+
declare const pendingSaves: Set<string>;
|
|
6
|
+
/** Connect-style middleware that handles POST <saveEndpoint> to write files to disk. */
|
|
7
|
+
declare function weslSaveMiddleware(projectRoot: string): (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { pendingSaves, weslSaveMiddleware };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import "./SaveEndpoint.mjs";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/SaveMiddleware.ts
|
|
5
|
+
/** Paths recently written by autosave; consumed by hotUpdate to suppress reloads. */
|
|
6
|
+
const pendingSaves = /* @__PURE__ */ new Set();
|
|
7
|
+
/** Connect-style middleware that handles POST <saveEndpoint> to write files to disk. */
|
|
8
|
+
function weslSaveMiddleware(projectRoot) {
|
|
9
|
+
return (req, res, next) => {
|
|
10
|
+
if (req.url !== "/__wgsl-edit/save") return next();
|
|
11
|
+
if (req.method !== "POST") return next();
|
|
12
|
+
readBody(req).then((body) => handleSave(body, projectRoot, res), next);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function handleSave(raw, projectRoot, res) {
|
|
16
|
+
let body;
|
|
17
|
+
try {
|
|
18
|
+
body = JSON.parse(raw);
|
|
19
|
+
} catch {
|
|
20
|
+
return respond(res, 400, "invalid JSON");
|
|
21
|
+
}
|
|
22
|
+
const { root, file, content } = body;
|
|
23
|
+
if (!file || typeof content !== "string") return respond(res, 400, "missing file or content");
|
|
24
|
+
const resolved = path.resolve(projectRoot, root ?? ".", file);
|
|
25
|
+
if (!resolved.startsWith(projectRoot + path.sep)) return respond(res, 403, "path outside project root");
|
|
26
|
+
try {
|
|
27
|
+
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
28
|
+
await fs.writeFile(resolved, content);
|
|
29
|
+
pendingSaves.add(resolved);
|
|
30
|
+
respond(res, 200, "ok");
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.error("[wgsl-edit] save failed:", e.message);
|
|
33
|
+
respond(res, 500, e.message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function readBody(req) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
const chunks = [];
|
|
39
|
+
req.on("data", (c) => chunks.push(c));
|
|
40
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString()));
|
|
41
|
+
req.on("error", reject);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function respond(res, status, msg) {
|
|
45
|
+
res.writeHead(status, { "Content-Type": "text/plain" });
|
|
46
|
+
res.end(msg);
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { pendingSaves, weslSaveMiddleware };
|