wesl-plugin 0.6.69 → 0.6.71
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 +3 -3
- package/dist/{PluginExtension-N4JBtncl.d.mts → PluginExtension-CHxUB0-8.d.mts} +19 -3
- package/dist/{StaticExtension-BvgvwnkI.mjs → StaticExtension-Q8HMuFJa.mjs} +57 -66
- package/dist/WeslPlugin-CajtfXHH.mjs +7344 -0
- package/dist/{WeslPluginOptions-DHLy1_Dm.d.mts → WeslPluginOptions-kd-CXSl9.d.mts} +1 -1
- package/dist/pluginIndex.d.mts +11 -12
- package/dist/pluginIndex.mjs +31 -2
- package/dist/plugins/astro.d.mts +1 -2
- package/dist/plugins/astro.mjs +3 -3
- package/dist/plugins/esbuild.d.mts +3 -4
- package/dist/plugins/esbuild.mjs +2 -2
- package/dist/plugins/farm.d.mts +1 -2
- package/dist/plugins/farm.mjs +2 -2
- package/dist/plugins/nuxt.d.mts +1 -2
- package/dist/plugins/nuxt.mjs +2 -2
- package/dist/plugins/rollup.d.mts +3 -4
- package/dist/plugins/rollup.mjs +2 -2
- package/dist/plugins/rspack.d.mts +1 -2
- package/dist/plugins/rspack.mjs +2 -2
- package/dist/plugins/vite.d.mts +3 -4
- package/dist/plugins/vite.mjs +2 -2
- package/dist/plugins/webpack.d.mts +3 -4
- package/dist/plugins/webpack.mjs +2 -2
- package/package.json +3 -2
- package/src/PluginApi.ts +111 -53
- package/src/PluginExtension.ts +21 -2
- package/src/WeslPlugin.ts +96 -146
- package/src/defaultSuffixTypes.d.ts +6 -0
- package/src/extensions/LinkExtension.ts +9 -16
- package/src/extensions/ReflectExtension.ts +53 -0
- package/src/extensions/StaticExtension.ts +16 -30
- package/src/pluginIndex.ts +1 -0
- package/dist/WeslPlugin-C1pAE34o.mjs +0 -10077
package/src/WeslPlugin.ts
CHANGED
|
@@ -19,23 +19,17 @@ import type { WeslPluginOptions } from "./WeslPluginOptions.ts";
|
|
|
19
19
|
|
|
20
20
|
export type { WeslToml, WeslTomlInfo };
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* The assumption is that the plugin is used for a single wesl.toml and set of shaders
|
|
24
|
-
* (a plugin instance supports only one shader project)
|
|
25
|
-
*/
|
|
22
|
+
/** Cache for a single plugin instance (one wesl.toml / shader project). */
|
|
26
23
|
interface PluginCache {
|
|
27
24
|
registry?: RecordResolver;
|
|
28
25
|
weslToml?: WeslTomlInfo;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
|
-
/** some types from unplugin */
|
|
32
28
|
type Resolver = (
|
|
33
29
|
this: UnpluginBuildContext & UnpluginContext,
|
|
34
30
|
id: string,
|
|
35
31
|
importer: string | undefined,
|
|
36
|
-
options: {
|
|
37
|
-
isEntry: boolean;
|
|
38
|
-
},
|
|
32
|
+
options: { isEntry: boolean },
|
|
39
33
|
) => Thenable<string | ExternalIdResult | null | undefined>;
|
|
40
34
|
|
|
41
35
|
type Loader = (
|
|
@@ -43,7 +37,7 @@ type Loader = (
|
|
|
43
37
|
id: string,
|
|
44
38
|
) => Thenable<TransformResult>;
|
|
45
39
|
|
|
46
|
-
/**
|
|
40
|
+
/** Shared state threaded through plugin functions. */
|
|
47
41
|
export interface PluginContext {
|
|
48
42
|
cache: PluginCache;
|
|
49
43
|
options: WeslPluginOptions;
|
|
@@ -54,18 +48,9 @@ export interface PluginContext {
|
|
|
54
48
|
|
|
55
49
|
type DebugLog = (msg: string, data?: Record<string, unknown>) => void;
|
|
56
50
|
|
|
57
|
-
/**
|
|
58
|
-
* A bundler plugin for processing WESL files.
|
|
59
|
-
*
|
|
60
|
-
* The plugin works by reading the wesl.toml file and possibly package.json
|
|
61
|
-
*
|
|
62
|
-
* The plugin is triggered by imports to special virtual module urls
|
|
63
|
-
* two urls suffixes are supported:
|
|
64
|
-
* 1. `import "./shaders/bar.wesl?reflect"` - produces a javascript file for binding struct reflection
|
|
65
|
-
* 2. `import "./shaders/bar.wesl?link"` - produces a javascript file for preconstructed link functions
|
|
66
|
-
*/
|
|
67
51
|
const builtinExtensions = [staticBuildExtension, linkBuildExtension];
|
|
68
52
|
|
|
53
|
+
/** Bundler plugin for WESL files, triggered by ?link or ?static import suffixes. */
|
|
69
54
|
export function weslPlugin(
|
|
70
55
|
options: WeslPluginOptions | undefined,
|
|
71
56
|
meta: UnpluginContextMeta,
|
|
@@ -85,13 +70,9 @@ export function weslPlugin(
|
|
|
85
70
|
load: buildLoader(context, log),
|
|
86
71
|
watchChange(id, _change) {
|
|
87
72
|
log("watchChange", { id });
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
cache.registry = undefined;
|
|
92
|
-
} else {
|
|
93
|
-
cache.registry = undefined;
|
|
94
|
-
}
|
|
73
|
+
// The cache is shared for multiple imports
|
|
74
|
+
if (id.endsWith("wesl.toml")) cache.weslToml = undefined;
|
|
75
|
+
cache.registry = undefined;
|
|
95
76
|
},
|
|
96
77
|
};
|
|
97
78
|
}
|
|
@@ -107,26 +88,59 @@ function pluginsByName(
|
|
|
107
88
|
return Object.fromEntries(entries);
|
|
108
89
|
}
|
|
109
90
|
|
|
110
|
-
/** wesl
|
|
111
|
-
|
|
112
|
-
* foo/bar.wesl?link
|
|
113
|
-
* or
|
|
114
|
-
* foo/bar.wesl COND=false ?static
|
|
115
|
-
*
|
|
116
|
-
* Bundlers may add extra query params (e.g. Vite adds ?import for dynamic imports,
|
|
117
|
-
* ?t=123 for cache busting), so we capture the full query and search within it.
|
|
118
|
-
*
|
|
119
|
-
* someday it'd be nice to support import attributes like:
|
|
120
|
-
* import "foo.bar.wesl?static" with { COND: false};
|
|
121
|
-
* (but that doesn't seem supported to be supported in the the bundler plugins yet)
|
|
122
|
-
*/
|
|
123
|
-
const pluginMatch =
|
|
124
|
-
/(^^)?(?<baseId>.*\.w[eg]sl)(?<cond>(\s*\w+(=\w+)?\s*)*)\?(?<query>.+)$/;
|
|
91
|
+
/** Match .wesl/.wgsl imports with query params. Bundlers may append extra params. */
|
|
92
|
+
const pluginMatch = /(^^)?(?<baseId>.*\.w[eg]sl)\?(?<query>.+)$/;
|
|
125
93
|
|
|
126
94
|
const resolvedPrefix = "^^";
|
|
127
95
|
|
|
128
|
-
/**
|
|
129
|
-
|
|
96
|
+
/** Reserved query param names (not treated as conditions). */
|
|
97
|
+
const reservedParams = new Set(["include"]);
|
|
98
|
+
|
|
99
|
+
interface ParsedQuery {
|
|
100
|
+
pluginName: string;
|
|
101
|
+
conditions?: Conditions;
|
|
102
|
+
options?: Record<string, string>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Parse query string into plugin name, conditions, and options. */
|
|
106
|
+
function parsePluginQuery(
|
|
107
|
+
query: string,
|
|
108
|
+
suffixes: string[],
|
|
109
|
+
): ParsedQuery | null {
|
|
110
|
+
const segments = query.split("&");
|
|
111
|
+
const pluginName = suffixes.find(s => segments.includes(s));
|
|
112
|
+
if (!pluginName) return null;
|
|
113
|
+
|
|
114
|
+
const isBundlerParam = (s: string) => s === "import" || /^t=\d+/.test(s);
|
|
115
|
+
const userSegments = segments.filter(
|
|
116
|
+
s => s !== pluginName && !isBundlerParam(s),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const conditions: Record<string, boolean> = {};
|
|
120
|
+
const options: Record<string, string> = {};
|
|
121
|
+
|
|
122
|
+
for (const seg of userSegments) {
|
|
123
|
+
const eqIdx = seg.indexOf("=");
|
|
124
|
+
if (eqIdx === -1) {
|
|
125
|
+
conditions[seg] = true; // bare name like "FUN" ==> condition true
|
|
126
|
+
} else {
|
|
127
|
+
const key = seg.slice(0, eqIdx);
|
|
128
|
+
const val = seg.slice(eqIdx + 1);
|
|
129
|
+
if (reservedParams.has(key)) options[key] = val;
|
|
130
|
+
else conditions[key] = val !== "false";
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const hasConds = Object.keys(conditions).length > 0;
|
|
135
|
+
const hasOpts = Object.keys(options).length > 0;
|
|
136
|
+
return {
|
|
137
|
+
pluginName,
|
|
138
|
+
conditions: hasConds ? conditions : undefined,
|
|
139
|
+
options: hasOpts ? options : undefined,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Build the resolveId hook for virtual module imports (?static, ?link, etc). */
|
|
130
144
|
function buildResolver(
|
|
131
145
|
options: WeslPluginOptions,
|
|
132
146
|
context: PluginContext,
|
|
@@ -135,71 +149,34 @@ function buildResolver(
|
|
|
135
149
|
const suffixes = pluginNames(options);
|
|
136
150
|
return resolver;
|
|
137
151
|
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
// but this does call resolver: import wgsl from "../shaders/foo/app.wesl MOBILE=true FUN SAFE=false ?static";
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* For imports with conditions, vite won't resolve the module-path part of the js import
|
|
144
|
-
* so we do it here.
|
|
145
|
-
*
|
|
146
|
-
* To avoid recirculating on resolve(), we rewrite the resolution id to start with ^^
|
|
147
|
-
* The loader will drop the prefix.
|
|
148
|
-
*/
|
|
152
|
+
// With pure query-param syntax, Vite resolves paths natively.
|
|
153
|
+
// The resolver is still needed for non-Vite bundlers that may not handle query params.
|
|
149
154
|
function resolver(
|
|
150
155
|
this: UnpluginBuildContext & UnpluginContext,
|
|
151
156
|
id: string,
|
|
152
157
|
importer: string | undefined,
|
|
153
158
|
): string | null {
|
|
154
|
-
if (id.startsWith(resolvedPrefix))
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return result;
|
|
172
|
-
}
|
|
173
|
-
return matched ? id : null; // this case doesn't happen AFAIK
|
|
159
|
+
if (id.startsWith(resolvedPrefix)) return id;
|
|
160
|
+
if (id === context.weslToml) return id;
|
|
161
|
+
|
|
162
|
+
const match = id.match(pluginMatch);
|
|
163
|
+
const query = match?.groups?.query;
|
|
164
|
+
if (!query) return null;
|
|
165
|
+
|
|
166
|
+
const parsed = parsePluginQuery(query, suffixes);
|
|
167
|
+
log("resolveId", { id, matched: !!parsed, suffixes });
|
|
168
|
+
if (!parsed) return null;
|
|
169
|
+
|
|
170
|
+
const baseId = match.groups!.baseId;
|
|
171
|
+
const importerDir = path.dirname(importer!);
|
|
172
|
+
const pathToShader = path.join(importerDir, baseId);
|
|
173
|
+
const result = resolvedPrefix + pathToShader + "?" + query;
|
|
174
|
+
log("resolveId resolved", { result });
|
|
175
|
+
return result;
|
|
174
176
|
}
|
|
175
177
|
}
|
|
176
178
|
|
|
177
|
-
|
|
178
|
-
baseId: string;
|
|
179
|
-
importParams?: string;
|
|
180
|
-
pluginName: string;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** Find matching plugin suffix in query string (handles ?import&static, ?t=123&static, etc.) */
|
|
184
|
-
function pluginSuffixMatch(id: string, suffixes: string[]): PluginMatch | null {
|
|
185
|
-
const match = id.match(pluginMatch);
|
|
186
|
-
const query = match?.groups?.query;
|
|
187
|
-
if (!query) return null;
|
|
188
|
-
|
|
189
|
-
// Query params are &-separated; find one that matches a configured suffix
|
|
190
|
-
const segments = query.split("&");
|
|
191
|
-
const pluginName = suffixes.find(s => segments.includes(s));
|
|
192
|
-
if (!pluginName) return null;
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
pluginName,
|
|
196
|
-
baseId: match.groups!.baseId,
|
|
197
|
-
importParams: match.groups?.cond,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/** build plugin function for serving a javascript module in response to
|
|
202
|
-
* an import of of our virtual import modules. */
|
|
179
|
+
/** Build the load hook that emits JS for virtual module imports. */
|
|
203
180
|
function buildLoader(context: PluginContext, log: DebugLog): Loader {
|
|
204
181
|
const { options } = context;
|
|
205
182
|
const suffixes = pluginNames(options);
|
|
@@ -210,50 +187,27 @@ function buildLoader(context: PluginContext, log: DebugLog): Loader {
|
|
|
210
187
|
this: UnpluginBuildContext & UnpluginContext,
|
|
211
188
|
id: string,
|
|
212
189
|
) {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
190
|
+
const match = id.match(pluginMatch);
|
|
191
|
+
const query = match?.groups?.query;
|
|
192
|
+
if (!query) return null;
|
|
193
|
+
|
|
194
|
+
const parsed = parsePluginQuery(query, suffixes);
|
|
195
|
+
log("load", { id, matched: parsed?.pluginName ?? null });
|
|
196
|
+
if (!parsed) return null;
|
|
197
|
+
|
|
198
|
+
const buildPluginApi = buildApi(context, this);
|
|
199
|
+
const plugin = pluginsMap[parsed.pluginName];
|
|
200
|
+
const rawPath = match.groups!.baseId;
|
|
201
|
+
const shaderPath = rawPath.startsWith(resolvedPrefix)
|
|
202
|
+
? rawPath.slice(resolvedPrefix.length)
|
|
203
|
+
: rawPath;
|
|
204
|
+
|
|
205
|
+
const { conditions, options: opts } = parsed;
|
|
206
|
+
log("load emitting", { shaderPath, conditions, options: opts });
|
|
207
|
+
return await plugin.emitFn(shaderPath, buildPluginApi, conditions, opts);
|
|
229
208
|
}
|
|
230
209
|
}
|
|
231
210
|
|
|
232
|
-
/**
|
|
233
|
-
* Convert an import parameters string to a Conditions record.
|
|
234
|
-
*
|
|
235
|
-
* Import parameters are key=value pairs separated by spaces.
|
|
236
|
-
* Values may be "true" or "false" or missing (default to true)
|
|
237
|
-
* e.g. ' MOBILE=true FUN SAFE=false '
|
|
238
|
-
*/
|
|
239
|
-
function importParamsToConditions(
|
|
240
|
-
importParams: string | undefined,
|
|
241
|
-
): Conditions | undefined {
|
|
242
|
-
if (!importParams) return undefined;
|
|
243
|
-
const params = importParams.trim().split(/\s+/);
|
|
244
|
-
const condEntries = params.map(p => {
|
|
245
|
-
const text = p.trim();
|
|
246
|
-
const [cond, value] = text.split("=");
|
|
247
|
-
if (value === undefined || value === "true") {
|
|
248
|
-
return [cond, true] as const;
|
|
249
|
-
} else {
|
|
250
|
-
return [cond, false] as const;
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
const conditions = Object.fromEntries(condEntries);
|
|
254
|
-
return conditions;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
211
|
function fmtDebugData(data?: Record<string, unknown>): string {
|
|
258
212
|
return data ? " " + JSON.stringify(data) : "";
|
|
259
213
|
}
|
|
@@ -264,9 +218,5 @@ function debugLog(msg: string, data?: Record<string, unknown>): void {
|
|
|
264
218
|
|
|
265
219
|
function noopLog(): void {}
|
|
266
220
|
|
|
267
|
-
export const unplugin = createUnplugin(
|
|
268
|
-
(options: WeslPluginOptions, meta: UnpluginContextMeta) => {
|
|
269
|
-
return weslPlugin(options, meta);
|
|
270
|
-
},
|
|
271
|
-
);
|
|
221
|
+
export const unplugin = createUnplugin(weslPlugin);
|
|
272
222
|
export default unplugin;
|
|
@@ -10,6 +10,12 @@ declare module "*?static" {
|
|
|
10
10
|
export default wgsl;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** @hidden */
|
|
14
|
+
declare module "*?simple_reflect" {
|
|
15
|
+
import type { WeslStruct } from "wesl-reflect";
|
|
16
|
+
export const structs: WeslStruct[];
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
/** @hidden */ // LATER move to separate package
|
|
14
20
|
declare module "*?bindingLayout" {
|
|
15
21
|
export const layouts: Record<string, GPUBindGroupLayoutEntry[]>;
|
|
@@ -14,18 +14,18 @@ export const linkBuildExtension: PluginExtension = {
|
|
|
14
14
|
async function emitLinkJs(
|
|
15
15
|
baseId: string,
|
|
16
16
|
api: PluginExtensionApi,
|
|
17
|
+
_conditions?: Record<string, boolean>,
|
|
18
|
+
options?: Record<string, string>,
|
|
17
19
|
): Promise<string> {
|
|
18
|
-
const { resolvedRoot, tomlDir } = await api.weslToml();
|
|
19
|
-
|
|
20
|
-
const weslSrc = await api.weslSrc();
|
|
21
|
-
|
|
22
20
|
const rootModule = await api.weslMain(baseId);
|
|
23
21
|
const rootModuleName = noSuffix(rootModule);
|
|
24
22
|
|
|
25
|
-
const
|
|
26
|
-
|
|
23
|
+
const [{ weslSrc, dependencies: autoDeps }, debugWeslRoot] =
|
|
24
|
+
await Promise.all([
|
|
25
|
+
api.fetchProject(rootModuleName, options),
|
|
26
|
+
api.debugWeslRoot(),
|
|
27
|
+
]);
|
|
27
28
|
|
|
28
|
-
const autoDeps = await api.weslDependencies();
|
|
29
29
|
const sanitizedDeps = autoDeps.map(dep => dep.replaceAll("/", "_"));
|
|
30
30
|
|
|
31
31
|
const bundleImports = autoDeps
|
|
@@ -35,25 +35,18 @@ async function emitLinkJs(
|
|
|
35
35
|
const rootName = path.basename(rootModuleName).replace(/\W/g, "_");
|
|
36
36
|
const paramsName = `link${rootName}Config`;
|
|
37
37
|
|
|
38
|
-
const linkParams: LinkParams = {
|
|
39
|
-
rootModuleName,
|
|
40
|
-
weslSrc,
|
|
41
|
-
debugWeslRoot,
|
|
42
|
-
};
|
|
43
|
-
|
|
38
|
+
const linkParams: LinkParams = { rootModuleName, weslSrc, debugWeslRoot };
|
|
44
39
|
const libsStr = `libs: [${sanitizedDeps.join(", ")}]`;
|
|
45
40
|
const linkParamsStr = `{
|
|
46
41
|
${serializeFields(linkParams)},
|
|
47
42
|
${libsStr},
|
|
48
43
|
}`;
|
|
49
44
|
|
|
50
|
-
|
|
45
|
+
return `
|
|
51
46
|
${bundleImports}
|
|
52
47
|
export const ${paramsName} = ${linkParamsStr};
|
|
53
48
|
export default ${paramsName};
|
|
54
49
|
`;
|
|
55
|
-
|
|
56
|
-
return src;
|
|
57
50
|
}
|
|
58
51
|
|
|
59
52
|
function serializeFields(record: Record<string, any>) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import type { StructElem } from "wesl";
|
|
3
|
+
import { originalTypeName, weslStructs, wgslTypeToTs } from "wesl-reflect";
|
|
4
|
+
import type {
|
|
5
|
+
PluginExtension,
|
|
6
|
+
PluginExtensionApi,
|
|
7
|
+
} from "../PluginExtension.ts";
|
|
8
|
+
|
|
9
|
+
export interface SimpleReflectOptions {
|
|
10
|
+
/** directory to contain the .d.ts files or undefined to not write .d.ts files */
|
|
11
|
+
typesDir?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** wesl-js build extension to reflect wgsl structs into js and .d.ts files. */
|
|
15
|
+
export function simpleReflect(
|
|
16
|
+
options: SimpleReflectOptions = {},
|
|
17
|
+
): PluginExtension {
|
|
18
|
+
const { typesDir = "./src/types" } = options;
|
|
19
|
+
return {
|
|
20
|
+
extensionName: "simple_reflect",
|
|
21
|
+
emitFn: async (_baseId: string, api: PluginExtensionApi) => {
|
|
22
|
+
const registry = await api.weslRegistry();
|
|
23
|
+
const astStructs = [...registry.allModules()].flatMap(([, module]) =>
|
|
24
|
+
module.moduleElem.contents.filter(
|
|
25
|
+
(e): e is StructElem => e.kind === "struct",
|
|
26
|
+
),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const jsStructs = weslStructs(astStructs);
|
|
30
|
+
if (typesDir) await writeTypes(astStructs, typesDir);
|
|
31
|
+
|
|
32
|
+
const structArray = JSON.stringify(jsStructs, null, 2);
|
|
33
|
+
return `export const structs = ${structArray};`;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Write .d.ts file with TypeScript interfaces for reflected structs. */
|
|
39
|
+
async function writeTypes(
|
|
40
|
+
structs: StructElem[],
|
|
41
|
+
typesDir: string,
|
|
42
|
+
): Promise<void> {
|
|
43
|
+
const tsdInterfaces = structs.map(s => {
|
|
44
|
+
const name = s.name.ident.originalName;
|
|
45
|
+
const entries = s.members.map(m => {
|
|
46
|
+
const tsType = wgslTypeToTs(originalTypeName(m.typeRef));
|
|
47
|
+
return ` ${m.name.name}: ${tsType};`;
|
|
48
|
+
});
|
|
49
|
+
return `interface ${name} {\n${entries.join("\n")}\n}`;
|
|
50
|
+
});
|
|
51
|
+
await fs.mkdir(typesDir, { recursive: true });
|
|
52
|
+
await fs.writeFile(`${typesDir}/reflectTypes.d.ts`, tsdInterfaces.join("\n"));
|
|
53
|
+
}
|
|
@@ -7,64 +7,50 @@ import type {
|
|
|
7
7
|
PluginExtensionApi,
|
|
8
8
|
} from "../PluginExtension.ts";
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* a wesl-js ?static build extension that statically links from the root file
|
|
12
|
-
* and emits a JavaScript file containing the linked wgsl string.
|
|
13
|
-
*
|
|
14
|
-
* use it like this:
|
|
15
|
-
* import wgsl from "./shaders/app.wesl?static";
|
|
16
|
-
*
|
|
17
|
-
* or with conditions, like this:
|
|
18
|
-
* import wgsl from "../shaders/foo/app.wesl MOBILE=true FUN SAFE=false ?static";
|
|
19
|
-
*/
|
|
10
|
+
/** Build extension for ?static imports: links WESL at build time, emits WGSL string. */
|
|
20
11
|
export const staticBuildExtension: PluginExtension = {
|
|
21
12
|
extensionName: "static",
|
|
22
13
|
emitFn: emitStaticJs,
|
|
23
14
|
};
|
|
24
15
|
|
|
25
|
-
/** Emit a
|
|
16
|
+
/** Emit a JS module exporting the statically linked WGSL string. */
|
|
26
17
|
async function emitStaticJs(
|
|
27
18
|
baseId: string,
|
|
28
19
|
api: PluginExtensionApi,
|
|
29
20
|
conditions?: Conditions,
|
|
21
|
+
_options?: Record<string, string>,
|
|
30
22
|
): Promise<string> {
|
|
31
23
|
const { resolvedRoot, tomlDir } = await api.weslToml();
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
const parentModule =
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
const tomlUrl = url.pathToFileURL(path.join(tomlDir, "wesl.toml"));
|
|
26
|
+
const parentModule = tomlUrl.toString();
|
|
27
|
+
|
|
28
|
+
const rootModule = await api.weslMain(baseId);
|
|
29
|
+
const rootModuleName = noSuffix(rootModule);
|
|
30
|
+
|
|
31
|
+
const [weslSrc, dependencies] = await Promise.all([
|
|
32
|
+
api.weslSrc(),
|
|
33
|
+
api.weslDependencies(),
|
|
34
|
+
]);
|
|
37
35
|
|
|
38
|
-
const dependencies = await api.weslDependencies();
|
|
39
36
|
const libFileUrls = dependencies.map(d => resolve(d, parentModule));
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
const futureLibs = libFileUrls.map(f => import(f));
|
|
43
|
-
const libModules = await Promise.all(futureLibs);
|
|
38
|
+
const libModules = await Promise.all(libFileUrls.map(f => import(f)));
|
|
44
39
|
const libs = libModules.map(m => m.default);
|
|
45
40
|
|
|
46
|
-
// find weslSrc and rootModule
|
|
47
|
-
const weslSrc = await api.weslSrc();
|
|
48
|
-
const rootModule = await api.weslMain(baseId);
|
|
49
|
-
const rootModuleName = noSuffix(rootModule);
|
|
50
|
-
|
|
51
|
-
// find weslRoot
|
|
52
41
|
const tomlRelative = path.relative(tomlDir, resolvedRoot);
|
|
53
42
|
const debugWeslRoot = tomlRelative.replaceAll(path.sep, "/");
|
|
54
43
|
|
|
55
|
-
const
|
|
44
|
+
const { dest: wgsl } = await link({
|
|
56
45
|
weslSrc,
|
|
57
46
|
rootModuleName,
|
|
58
47
|
debugWeslRoot,
|
|
59
48
|
libs,
|
|
60
49
|
conditions,
|
|
61
50
|
});
|
|
62
|
-
const wgsl = result.dest;
|
|
63
51
|
|
|
64
|
-
|
|
52
|
+
return `
|
|
65
53
|
export const wgsl = \`${wgsl}\`;
|
|
66
54
|
export default wgsl;
|
|
67
55
|
`;
|
|
68
|
-
|
|
69
|
-
return src;
|
|
70
56
|
}
|
package/src/pluginIndex.ts
CHANGED