wesl 0.7.23 → 0.7.24
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/dist/index.d.ts +8 -1
- package/dist/index.js +26 -69
- package/dist/weslBundleDeclUrl.js +1 -2
- package/package.json +1 -1
- package/src/ModuleResolver.ts +21 -0
- package/src/test/FreshResolver.test.ts +43 -0
- package/src/test/TestLink.ts +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -821,6 +821,13 @@ declare class BundleResolver implements ModuleResolver {
|
|
|
821
821
|
private moduleToFilePath;
|
|
822
822
|
private modulePathToDebugPath;
|
|
823
823
|
}
|
|
824
|
+
/** Wrap a resolver so each resolveModule call returns a freshly parsed AST.
|
|
825
|
+
* Use this when reusing a resolver across multiple link() calls, since binding
|
|
826
|
+
* mutates ASTs in place. Each wrapper instance caches within itself, so within
|
|
827
|
+
* a single link pass the same module path returns the same AST object.
|
|
828
|
+
* Re-parsing is faster than structuredClone due to cycle-tracking overhead
|
|
829
|
+
* from Scope.parent backpointers. */
|
|
830
|
+
declare function freshResolver(inner: ModuleResolver): ModuleResolver;
|
|
824
831
|
/** Convert file path to module path (e.g., "foo/bar.wesl" to "package::foo::bar"). */
|
|
825
832
|
declare function fileToModulePath(filePath: string, packageName: string, treatLibAsRoot: boolean): string;
|
|
826
833
|
//#endregion
|
|
@@ -1275,4 +1282,4 @@ declare function offsetToLineNumber(offset: number, text: string): [lineNum: num
|
|
|
1275
1282
|
*/
|
|
1276
1283
|
declare function errorHighlight(source: string, span: Span): [string, string];
|
|
1277
1284
|
//#endregion
|
|
1278
|
-
export { AbstractElem, AbstractElemBase, AliasElem, Attribute, AttributeElem, BatchModuleResolver, BinaryExpression, BinaryOperator, BindIdentsParams, BindResults, BindingAST, BindingStructElem, BlockStatement, BoundAndTransformed, BuiltinAttribute, BundleResolver, ComponentExpression, ComponentMemberExpression, CompositeResolver, ConditionalAttribute, Conditions, ConstAssertElem, ConstElem, ContainerElem, ContinuingElem, DeclIdent, DeclIdentElem, DeclarationElem, DiagnosticAttribute, DiagnosticDirective, DiagnosticRule, DirectiveElem, DirectiveVariant, ElemKindMap, ElemWithAttributes, ElemWithContentsBase, ElifAttribute, ElseAttribute, EmittableElem, EnableDirective, ExpressionElem, ExtendedGPUValidationError, FnElem, FnParamElem, FunctionCallExpression, GlobalDeclarationElem, GlobalVarElem, GrammarElem, HasAttributes, Ident, IfAttribute, ImportCollection, ImportElem, ImportItem, ImportSegment, ImportStatement, InterpolateAttribute, LetElem, LexicalScope, LinkConfig, LinkParams, LinkRegistryParams, LinkedWesl, LinkerTransform, Literal, LiveDecls, ManglerFn, ModuleElem, ModuleResolver, NameElem, OpenElem, OverrideElem, ParenthesizedExpression, ParseError, type ParseOptions, PartialScope, RecordResolver, RecordResolverOptions, RefIdent, RefIdentElem, RequiresDirective, Scope, ScopeItem, SimpleMemberRef, Span, SrcMap, SrcMapBuilder, SrcMapEntry, SrcModule, SrcPosition, SrcWithPath, StableState, StandardAttribute, StatementElem, StructElem, StructMemberElem, StuffElem, SwitchClauseElem, SyntheticElem, TerminalElem, TextElem, TrackingResolver, TransformedAST, TranslateTimeExpressionElem, TypeRefElem, TypeTemplateParameter, TypedDeclElem, UnaryExpression, UnaryOperator, UnboundRef, UnknownExpressionElem, VarElem, VirtualLibrary, VirtualLibraryFn, VirtualLibrarySet, WeslAST, WeslBundle, WeslDevice, WeslGPUCompilationInfo, WeslGPUCompilationMessage, WeslJsPlugin, WeslParseContext, WeslParseError, WeslParseState, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, childIdent, childScope, containsScope, debug, debugContentsToString, discoverModules, emptyScope, errorHighlight, fileToModulePath, filterMap, filterValidElements, findAllRootDecls, findMap, findRefsToBindingStructs, findUnboundIdents, findUnboundRefs, findValidRootDecls, flatImports, groupBy, grouped, identToString, last, lengthPrefixMangle, link, linkRegistry, liveDeclsToString, log, lowerBindingStructs, makeLiveDecls, makeWeslDevice, mapForward, mapValues, markBindingStructs, markEntryTypes, mergeScope, minimalMangle, minimallyMangledName, modulePartsToRelativePath, moduleToRelativePath, multiKeySet, multisampledTextureTypes, nextIdentId, noSuffix, normalize, normalizeDebugRoot, normalizeModuleName, npmNameVariations, offsetToLineNumber, overlapTail, parseSrcModule, partition, publicDecl, replaceWords, requestWeslDevice, resetScopeIds, resolveModulePath, sampledTextureTypes, sanitizePackageName, scan, scopeToString, scopeToStringLong, srcLog, stdEnumerant, stdEnumerants, stdFn, stdFns, stdType, stdTypes, textureStorageTypes, transformBindingReference, transformBindingStruct, underscoreMangle, validation, wgslStandardAttributes, withLoggerAsync };
|
|
1285
|
+
export { AbstractElem, AbstractElemBase, AliasElem, Attribute, AttributeElem, BatchModuleResolver, BinaryExpression, BinaryOperator, BindIdentsParams, BindResults, BindingAST, BindingStructElem, BlockStatement, BoundAndTransformed, BuiltinAttribute, BundleResolver, ComponentExpression, ComponentMemberExpression, CompositeResolver, ConditionalAttribute, Conditions, ConstAssertElem, ConstElem, ContainerElem, ContinuingElem, DeclIdent, DeclIdentElem, DeclarationElem, DiagnosticAttribute, DiagnosticDirective, DiagnosticRule, DirectiveElem, DirectiveVariant, ElemKindMap, ElemWithAttributes, ElemWithContentsBase, ElifAttribute, ElseAttribute, EmittableElem, EnableDirective, ExpressionElem, ExtendedGPUValidationError, FnElem, FnParamElem, FunctionCallExpression, GlobalDeclarationElem, GlobalVarElem, GrammarElem, HasAttributes, Ident, IfAttribute, ImportCollection, ImportElem, ImportItem, ImportSegment, ImportStatement, InterpolateAttribute, LetElem, LexicalScope, LinkConfig, LinkParams, LinkRegistryParams, LinkedWesl, LinkerTransform, Literal, LiveDecls, ManglerFn, ModuleElem, ModuleResolver, NameElem, OpenElem, OverrideElem, ParenthesizedExpression, ParseError, type ParseOptions, PartialScope, RecordResolver, RecordResolverOptions, RefIdent, RefIdentElem, RequiresDirective, Scope, ScopeItem, SimpleMemberRef, Span, SrcMap, SrcMapBuilder, SrcMapEntry, SrcModule, SrcPosition, SrcWithPath, StableState, StandardAttribute, StatementElem, StructElem, StructMemberElem, StuffElem, SwitchClauseElem, SyntheticElem, TerminalElem, TextElem, TrackingResolver, TransformedAST, TranslateTimeExpressionElem, TypeRefElem, TypeTemplateParameter, TypedDeclElem, UnaryExpression, UnaryOperator, UnboundRef, UnknownExpressionElem, VarElem, VirtualLibrary, VirtualLibraryFn, VirtualLibrarySet, WeslAST, WeslBundle, WeslDevice, WeslGPUCompilationInfo, WeslGPUCompilationMessage, WeslJsPlugin, WeslParseContext, WeslParseError, WeslParseState, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, childIdent, childScope, containsScope, debug, debugContentsToString, discoverModules, emptyScope, errorHighlight, fileToModulePath, filterMap, filterValidElements, findAllRootDecls, findMap, findRefsToBindingStructs, findUnboundIdents, findUnboundRefs, findValidRootDecls, flatImports, freshResolver, groupBy, grouped, identToString, last, lengthPrefixMangle, link, linkRegistry, liveDeclsToString, log, lowerBindingStructs, makeLiveDecls, makeWeslDevice, mapForward, mapValues, markBindingStructs, markEntryTypes, mergeScope, minimalMangle, minimallyMangledName, modulePartsToRelativePath, moduleToRelativePath, multiKeySet, multisampledTextureTypes, nextIdentId, noSuffix, normalize, normalizeDebugRoot, normalizeModuleName, npmNameVariations, offsetToLineNumber, overlapTail, parseSrcModule, partition, publicDecl, replaceWords, requestWeslDevice, resetScopeIds, resolveModulePath, sampledTextureTypes, sanitizePackageName, scan, scopeToString, scopeToStringLong, srcLog, stdEnumerant, stdEnumerants, stdFn, stdFns, stdType, stdTypes, textureStorageTypes, transformBindingReference, transformBindingStruct, underscoreMangle, validation, wgslStandardAttributes, withLoggerAsync };
|
package/dist/index.js
CHANGED
|
@@ -85,7 +85,6 @@ function getStarts(src) {
|
|
|
85
85
|
startCache.set(src, starts);
|
|
86
86
|
return starts;
|
|
87
87
|
}
|
|
88
|
-
|
|
89
88
|
//#endregion
|
|
90
89
|
//#region src/Assertions.ts
|
|
91
90
|
/** checks whether a condition is true, otherwise throws */
|
|
@@ -94,7 +93,7 @@ function assertThat(condition, msg) {
|
|
|
94
93
|
}
|
|
95
94
|
/** when debug is enabled, checks whether a condition is true, otherwise throws */
|
|
96
95
|
function assertThatDebug(condition, msg) {
|
|
97
|
-
|
|
96
|
+
assertThat(condition, msg);
|
|
98
97
|
}
|
|
99
98
|
/**
|
|
100
99
|
* Useful to validate that all cases are handled,
|
|
@@ -112,7 +111,6 @@ var ErrorWithData = class extends Error {
|
|
|
112
111
|
this.data = options?.data;
|
|
113
112
|
}
|
|
114
113
|
};
|
|
115
|
-
|
|
116
114
|
//#endregion
|
|
117
115
|
//#region src/Util.ts
|
|
118
116
|
function multiKeySet(m, a, b, v) {
|
|
@@ -243,7 +241,6 @@ function errorHighlight(source, span) {
|
|
|
243
241
|
const linePos = Math.max(0, span[0] - lineStartOffset);
|
|
244
242
|
return [source.slice(lineStartOffset, lineEndOffset), " ".repeat(linePos) + "^".repeat(caretCount)];
|
|
245
243
|
}
|
|
246
|
-
|
|
247
244
|
//#endregion
|
|
248
245
|
//#region src/vlq/vlq.ts
|
|
249
246
|
/*!
|
|
@@ -280,7 +277,6 @@ function encode_integer(num) {
|
|
|
280
277
|
} while (enc > 0);
|
|
281
278
|
return result;
|
|
282
279
|
}
|
|
283
|
-
|
|
284
280
|
//#endregion
|
|
285
281
|
//#region src/ClickableError.ts
|
|
286
282
|
const isBrowser = "document" in globalThis;
|
|
@@ -295,7 +291,7 @@ function throwClickableError(params) {
|
|
|
295
291
|
length
|
|
296
292
|
};
|
|
297
293
|
error.weslLocation = weslLocation;
|
|
298
|
-
if (!
|
|
294
|
+
if (!isBrowser) throw error;
|
|
299
295
|
const mappings = encodeVlq([
|
|
300
296
|
0,
|
|
301
297
|
0,
|
|
@@ -328,7 +324,7 @@ function throwClickableError(params) {
|
|
|
328
324
|
} catch (e) {
|
|
329
325
|
if ("stackTraceLimit" in Error) Error.stackTraceLimit = oldLimit;
|
|
330
326
|
error.message = "";
|
|
331
|
-
|
|
327
|
+
e.cause = error;
|
|
332
328
|
e.weslLocation = weslLocation;
|
|
333
329
|
throw e;
|
|
334
330
|
}
|
|
@@ -356,7 +352,6 @@ function failIdentElem(identElem, msg = "") {
|
|
|
356
352
|
error: new Error(detailedMessage)
|
|
357
353
|
});
|
|
358
354
|
}
|
|
359
|
-
|
|
360
355
|
//#endregion
|
|
361
356
|
//#region src/Conditions.ts
|
|
362
357
|
/** @return true if the @if attribute is valid with current Conditions */
|
|
@@ -453,7 +448,6 @@ function findConditional(attributes) {
|
|
|
453
448
|
if (kind === "@if" || kind === "@elif" || kind === "@else") return attr.attribute;
|
|
454
449
|
}
|
|
455
450
|
}
|
|
456
|
-
|
|
457
451
|
//#endregion
|
|
458
452
|
//#region src/Scope.ts
|
|
459
453
|
/** Combine two scope siblings.
|
|
@@ -502,7 +496,6 @@ function childScope(child) {
|
|
|
502
496
|
function childIdent(child) {
|
|
503
497
|
return !childScope(child);
|
|
504
498
|
}
|
|
505
|
-
|
|
506
499
|
//#endregion
|
|
507
500
|
//#region src/StandardTypes.ts
|
|
508
501
|
const stdFns = `bitcast all any select arrayLength
|
|
@@ -603,7 +596,6 @@ function stdFn(name) {
|
|
|
603
596
|
function stdEnumerant(name) {
|
|
604
597
|
return stdEnumerants.includes(name);
|
|
605
598
|
}
|
|
606
|
-
|
|
607
599
|
//#endregion
|
|
608
600
|
//#region src/LowerAndEmit.ts
|
|
609
601
|
/** Traverse the AST, starting from root elements, emitting WGSL for each. */
|
|
@@ -975,7 +967,6 @@ function findDecl(ident) {
|
|
|
975
967
|
} while (i);
|
|
976
968
|
throw new Error(`unresolved identifer: ${ident.originalName}`);
|
|
977
969
|
}
|
|
978
|
-
|
|
979
970
|
//#endregion
|
|
980
971
|
//#region src/debug/ImportToString.ts
|
|
981
972
|
function importToString(tree) {
|
|
@@ -991,7 +982,6 @@ function segmentToString(segment) {
|
|
|
991
982
|
} else if (segment.kind === "import-collection") return `{${segment.subtrees.map((s) => importToStringImpl(s)).join(", ")}}`;
|
|
992
983
|
else assertUnreachable(segment);
|
|
993
984
|
}
|
|
994
|
-
|
|
995
985
|
//#endregion
|
|
996
986
|
//#region src/debug/LineWrapper.ts
|
|
997
987
|
/** debug utility for constructing strings that wrap at a fixed column width
|
|
@@ -1052,7 +1042,6 @@ function firstLineLength(s) {
|
|
|
1052
1042
|
const i = s.indexOf("\n");
|
|
1053
1043
|
return i === -1 ? s.length : i;
|
|
1054
1044
|
}
|
|
1055
|
-
|
|
1056
1045
|
//#endregion
|
|
1057
1046
|
//#region src/debug/ASTtoString.ts
|
|
1058
1047
|
const maxLineLength = 150;
|
|
@@ -1193,7 +1182,6 @@ function debugContentsToString(elem) {
|
|
|
1193
1182
|
return `?${c.kind}?`;
|
|
1194
1183
|
}).join(" ");
|
|
1195
1184
|
}
|
|
1196
|
-
|
|
1197
1185
|
//#endregion
|
|
1198
1186
|
//#region src/debug/ScopeToString.ts
|
|
1199
1187
|
/** A debugging print of the scope tree with identifiers in nested brackets */
|
|
@@ -1250,7 +1238,6 @@ function identToString(ident) {
|
|
|
1250
1238
|
return `%${originalName}${mangledName ? `(${mangledName})` : ""} ${idStr} `;
|
|
1251
1239
|
}
|
|
1252
1240
|
}
|
|
1253
|
-
|
|
1254
1241
|
//#endregion
|
|
1255
1242
|
//#region src/LiveDeclarations.ts
|
|
1256
1243
|
/** create a LiveDecls */
|
|
@@ -1265,7 +1252,6 @@ function liveDeclsToString(liveDecls) {
|
|
|
1265
1252
|
const { decls, parent } = liveDecls;
|
|
1266
1253
|
return `decls: { ${Array.from(decls.entries()).map(([name, decl]) => `${name}:${identToString(decl)}`).join(", ")} }, parent: ${parent ? liveDeclsToString(parent) : "null"}`;
|
|
1267
1254
|
}
|
|
1268
|
-
|
|
1269
1255
|
//#endregion
|
|
1270
1256
|
//#region src/Mangler.ts
|
|
1271
1257
|
/**
|
|
@@ -1307,7 +1293,6 @@ function minimallyMangledName(proposedName, globalNames) {
|
|
|
1307
1293
|
while (globalNames.has(renamed)) renamed = proposedName + conflicts++;
|
|
1308
1294
|
return renamed;
|
|
1309
1295
|
}
|
|
1310
|
-
|
|
1311
1296
|
//#endregion
|
|
1312
1297
|
//#region src/ModulePathUtil.ts
|
|
1313
1298
|
/** WESL module path utilities for converting between :: and / separators. */
|
|
@@ -1344,7 +1329,6 @@ function normalizeDebugRoot(root) {
|
|
|
1344
1329
|
if (root === "") return "";
|
|
1345
1330
|
return root.endsWith("/") ? root : root + "/";
|
|
1346
1331
|
}
|
|
1347
|
-
|
|
1348
1332
|
//#endregion
|
|
1349
1333
|
//#region src/FlattenTreeImport.ts
|
|
1350
1334
|
/**
|
|
@@ -1369,7 +1353,6 @@ function flattenTreeImport(imp) {
|
|
|
1369
1353
|
else assertUnreachable(finalSegment);
|
|
1370
1354
|
}
|
|
1371
1355
|
}
|
|
1372
|
-
|
|
1373
1356
|
//#endregion
|
|
1374
1357
|
//#region src/ParseError.ts
|
|
1375
1358
|
var ParseError = class extends Error {
|
|
@@ -1379,7 +1362,6 @@ var ParseError = class extends Error {
|
|
|
1379
1362
|
this.span = span;
|
|
1380
1363
|
}
|
|
1381
1364
|
};
|
|
1382
|
-
|
|
1383
1365
|
//#endregion
|
|
1384
1366
|
//#region src/parse/ContentsHelpers.ts
|
|
1385
1367
|
/** Push partial element onto stack for content collection. */
|
|
@@ -1429,7 +1411,6 @@ function coverWithText(ctx, contents, start, end) {
|
|
|
1429
1411
|
if (pos < end) elems.push(makeText(srcModule, pos, end));
|
|
1430
1412
|
return elems;
|
|
1431
1413
|
}
|
|
1432
|
-
|
|
1433
1414
|
//#endregion
|
|
1434
1415
|
//#region src/parse/ExpressionUtil.ts
|
|
1435
1416
|
function makeLiteral(token) {
|
|
@@ -1502,7 +1483,6 @@ function makeCallExpression(fn, templateArgs, args, end) {
|
|
|
1502
1483
|
end
|
|
1503
1484
|
};
|
|
1504
1485
|
}
|
|
1505
|
-
|
|
1506
1486
|
//#endregion
|
|
1507
1487
|
//#region src/parse/ParseUtil.ts
|
|
1508
1488
|
/** Match text and throw ParseError if not found. */
|
|
@@ -1603,7 +1583,6 @@ function linkDeclIdentElem(declIdentElem, declElem) {
|
|
|
1603
1583
|
function linkDeclIdent(typedDecl, declElem) {
|
|
1604
1584
|
linkDeclIdentElem(typedDecl.decl, declElem);
|
|
1605
1585
|
}
|
|
1606
|
-
|
|
1607
1586
|
//#endregion
|
|
1608
1587
|
//#region src/parse/OperatorBinding.ts
|
|
1609
1588
|
/**
|
|
@@ -1674,7 +1653,6 @@ function getOpGroup(op) {
|
|
|
1674
1653
|
}
|
|
1675
1654
|
/** Validate WGSL 8.19 operator binding restrictions, return op's group. */
|
|
1676
1655
|
function checkOpBinding(stream, op, leftGroup) {
|
|
1677
|
-
if (!validation) return leftGroup;
|
|
1678
1656
|
const opGroup = getOpGroup(op);
|
|
1679
1657
|
if (!canBindLeft(op, leftGroup)) throwParseError(stream, `'${op}' requires parentheses after ${leftGroup}`);
|
|
1680
1658
|
if (!canSelfChain(opGroup) && leftGroup === opGroup) throwParseError(stream, `'${op}' cannot be chained`);
|
|
@@ -1698,7 +1676,6 @@ function canSelfChain(group) {
|
|
|
1698
1676
|
function isBitwise(g) {
|
|
1699
1677
|
return g === "bitAnd" || g === "bitXor" || g === "bitOr";
|
|
1700
1678
|
}
|
|
1701
|
-
|
|
1702
1679
|
//#endregion
|
|
1703
1680
|
//#region src/parse/ParseIdent.ts
|
|
1704
1681
|
/** WESL keywords that cannot be used as path segment identifiers. */
|
|
@@ -1753,7 +1730,6 @@ function isPathSegment(token, isFirst) {
|
|
|
1753
1730
|
if (weslKeywords.has(token.text)) return isFirst && pathPrefixKeywords.has(token.text);
|
|
1754
1731
|
return true;
|
|
1755
1732
|
}
|
|
1756
|
-
|
|
1757
1733
|
//#endregion
|
|
1758
1734
|
//#region src/parse/ParseType.ts
|
|
1759
1735
|
/**
|
|
@@ -1796,7 +1772,6 @@ function parseTemplateParam(ctx) {
|
|
|
1796
1772
|
if (expr) return expr;
|
|
1797
1773
|
throwParseError(ctx.stream, "Expected expression in template parameters");
|
|
1798
1774
|
}
|
|
1799
|
-
|
|
1800
1775
|
//#endregion
|
|
1801
1776
|
//#region src/parse/ParseCall.ts
|
|
1802
1777
|
/**
|
|
@@ -1838,7 +1813,6 @@ function matchCloseParen(stream) {
|
|
|
1838
1813
|
const token = stream.matchText(")");
|
|
1839
1814
|
return token ? token.span[1] : null;
|
|
1840
1815
|
}
|
|
1841
|
-
|
|
1842
1816
|
//#endregion
|
|
1843
1817
|
//#region src/parse/ParseExpression.ts
|
|
1844
1818
|
/**
|
|
@@ -2002,7 +1976,6 @@ function parseIndexAccess(ctx, base) {
|
|
|
2002
1976
|
if (!stream.matchText("[")) return null;
|
|
2003
1977
|
return makeComponentExpression(base, expectExpression(ctx, "Expected expression in array index"), expect(stream, "]", "array index").span[1]);
|
|
2004
1978
|
}
|
|
2005
|
-
|
|
2006
1979
|
//#endregion
|
|
2007
1980
|
//#region src/parse/ParseAttribute.ts
|
|
2008
1981
|
/** Grammar: attribute * */
|
|
@@ -2190,7 +2163,6 @@ function parseAttrParam(ctx) {
|
|
|
2190
2163
|
contents: finishContents(ctx, start, end)
|
|
2191
2164
|
};
|
|
2192
2165
|
}
|
|
2193
|
-
|
|
2194
2166
|
//#endregion
|
|
2195
2167
|
//#region src/parse/ParseDirective.ts
|
|
2196
2168
|
/** Grammar: global_directive : diagnostic_directive | enable_directive | requires_directive */
|
|
@@ -2251,7 +2223,6 @@ function makeDirectiveElem(directive, token, stream, attributes) {
|
|
|
2251
2223
|
attachAttributes(elem, attributes);
|
|
2252
2224
|
return elem;
|
|
2253
2225
|
}
|
|
2254
|
-
|
|
2255
2226
|
//#endregion
|
|
2256
2227
|
//#region src/parse/ParseControlFlow.ts
|
|
2257
2228
|
/**
|
|
@@ -2334,7 +2305,6 @@ function parseCaseBody(ctx, errorMsg) {
|
|
|
2334
2305
|
if (!body) throwParseError(ctx.stream, errorMsg);
|
|
2335
2306
|
ctx.addElem(body);
|
|
2336
2307
|
}
|
|
2337
|
-
|
|
2338
2308
|
//#endregion
|
|
2339
2309
|
//#region src/parse/ParseValueDeclaration.ts
|
|
2340
2310
|
/** Grammar: 'const' optionally_typed_ident '=' expression (global or local) */
|
|
@@ -2417,7 +2387,6 @@ function parseOptionalType(ctx) {
|
|
|
2417
2387
|
typeScope
|
|
2418
2388
|
};
|
|
2419
2389
|
}
|
|
2420
|
-
|
|
2421
2390
|
//#endregion
|
|
2422
2391
|
//#region src/parse/ParseGlobalVar.ts
|
|
2423
2392
|
/**
|
|
@@ -2496,7 +2465,6 @@ function skipTemplateList(ctx) {
|
|
|
2496
2465
|
stream.nextToken();
|
|
2497
2466
|
}
|
|
2498
2467
|
}
|
|
2499
|
-
|
|
2500
2468
|
//#endregion
|
|
2501
2469
|
//#region src/parse/ParseLocalVar.ts
|
|
2502
2470
|
/**
|
|
@@ -2531,7 +2499,6 @@ function parseVarOrLet(ctx, keyword, hasTemplate, requiresInit, attributes) {
|
|
|
2531
2499
|
linkDeclIdent(typedDecl, elem);
|
|
2532
2500
|
return elem;
|
|
2533
2501
|
}
|
|
2534
|
-
|
|
2535
2502
|
//#endregion
|
|
2536
2503
|
//#region src/parse/ParseSimpleStatement.ts
|
|
2537
2504
|
const assignmentOps = new Set([
|
|
@@ -2641,7 +2608,6 @@ function parseAssignmentRhs(ctx) {
|
|
|
2641
2608
|
function parseIncDecOperator(stream) {
|
|
2642
2609
|
return !!stream.nextIf(({ text }) => text === "++" || text === "--");
|
|
2643
2610
|
}
|
|
2644
|
-
|
|
2645
2611
|
//#endregion
|
|
2646
2612
|
//#region src/parse/ParseLoop.ts
|
|
2647
2613
|
/**
|
|
@@ -2710,7 +2676,6 @@ function parseForUpdate(ctx) {
|
|
|
2710
2676
|
if (expr && ctx.options.preserveExpressions) ctx.addElem(expr);
|
|
2711
2677
|
parseIncDecOperator(ctx.stream) || parseAssignmentRhs(ctx);
|
|
2712
2678
|
}
|
|
2713
|
-
|
|
2714
2679
|
//#endregion
|
|
2715
2680
|
//#region src/parse/ParseStatement.ts
|
|
2716
2681
|
/** Function bodies share scope with parameters (per WGSL spec). */
|
|
@@ -2820,7 +2785,6 @@ function finalizeConditional$1(ctx, hasConditional, attributes) {
|
|
|
2820
2785
|
function getConditionalAttribute(attributes) {
|
|
2821
2786
|
return attributes.find((a) => isConditionalAttribute$1(a.attribute))?.attribute;
|
|
2822
2787
|
}
|
|
2823
|
-
|
|
2824
2788
|
//#endregion
|
|
2825
2789
|
//#region src/parse/ParseFn.ts
|
|
2826
2790
|
/**
|
|
@@ -2917,7 +2881,6 @@ function parseFnParam(ctx) {
|
|
|
2917
2881
|
attachAttributes(elem, attributes.length > 0 ? attributes : void 0);
|
|
2918
2882
|
return elem;
|
|
2919
2883
|
}
|
|
2920
|
-
|
|
2921
2884
|
//#endregion
|
|
2922
2885
|
//#region src/parse/ParseImport.ts
|
|
2923
2886
|
/** WESL Grammar: translation_unit : import_statement* global_directive* global_decl* */
|
|
@@ -3043,7 +3006,6 @@ function makeImportItem(name, as) {
|
|
|
3043
3006
|
as
|
|
3044
3007
|
};
|
|
3045
3008
|
}
|
|
3046
|
-
|
|
3047
3009
|
//#endregion
|
|
3048
3010
|
//#region src/parse/ParseStruct.ts
|
|
3049
3011
|
/**
|
|
@@ -3104,7 +3066,6 @@ function parseStructMember(ctx) {
|
|
|
3104
3066
|
attachAttributes(elem, attributes.length ? attributes : void 0);
|
|
3105
3067
|
return elem;
|
|
3106
3068
|
}
|
|
3107
|
-
|
|
3108
3069
|
//#endregion
|
|
3109
3070
|
//#region src/parse/ParseModule.ts
|
|
3110
3071
|
const declParsers = [
|
|
@@ -3176,7 +3137,6 @@ function recordDecl(ctx, elem, attrs) {
|
|
|
3176
3137
|
function isConditionalAttribute(a) {
|
|
3177
3138
|
return a.kind === "@if" || a.kind === "@elif" || a.kind === "@else";
|
|
3178
3139
|
}
|
|
3179
|
-
|
|
3180
3140
|
//#endregion
|
|
3181
3141
|
//#region src/parse/ParsingContext.ts
|
|
3182
3142
|
/** Context for parsers to build AST and manage scopes. */
|
|
@@ -3248,7 +3208,6 @@ var ParsingContext = class {
|
|
|
3248
3208
|
this.state.context.scope.contents.push(ident);
|
|
3249
3209
|
}
|
|
3250
3210
|
};
|
|
3251
|
-
|
|
3252
3211
|
//#endregion
|
|
3253
3212
|
//#region src/parse/Keywords.ts
|
|
3254
3213
|
/** https://www.w3.org/TR/WGSL/#keyword-summary */
|
|
@@ -3269,7 +3228,6 @@ const reservedWords = `NULL Self abstract active alignas alignof as asm asm_frag
|
|
|
3269
3228
|
self set shared sizeof smooth snorm static static_assert static_cast std subroutine super
|
|
3270
3229
|
target template this thread_local throw trait try type typedef typeid typename typeof
|
|
3271
3230
|
union unless unorm unsafe unsized use using varying virtual volatile wgsl where with writeonly yield`.split(/\s+/);
|
|
3272
|
-
|
|
3273
3231
|
//#endregion
|
|
3274
3232
|
//#region src/parse/stream/CachingStream.ts
|
|
3275
3233
|
var CachingStream = class {
|
|
@@ -3319,7 +3277,6 @@ var Cache = class extends Map {
|
|
|
3319
3277
|
return super.set(k, v);
|
|
3320
3278
|
}
|
|
3321
3279
|
};
|
|
3322
|
-
|
|
3323
3280
|
//#endregion
|
|
3324
3281
|
//#region src/parse/stream/RegexHelpers.ts
|
|
3325
3282
|
function toRegexSource(nameExp) {
|
|
@@ -3349,7 +3306,6 @@ function matchOneOf(syms) {
|
|
|
3349
3306
|
const escaped = syms.split(/\s+/).sort((a, b) => b.length - a.length).filter((s) => s).map(escapeRegex);
|
|
3350
3307
|
return new RegExp(escaped.join("|"));
|
|
3351
3308
|
}
|
|
3352
|
-
|
|
3353
3309
|
//#endregion
|
|
3354
3310
|
//#region src/parse/stream/MatchersStream.ts
|
|
3355
3311
|
/** Runs a `RegexMatchers` on an input string */
|
|
@@ -3412,7 +3368,6 @@ function findGroupDex(indices) {
|
|
|
3412
3368
|
};
|
|
3413
3369
|
}
|
|
3414
3370
|
}
|
|
3415
|
-
|
|
3416
3371
|
//#endregion
|
|
3417
3372
|
//#region src/parse/WeslStream.ts
|
|
3418
3373
|
/** Whitespaces including new lines */
|
|
@@ -3596,7 +3551,6 @@ var WeslStream = class {
|
|
|
3596
3551
|
}
|
|
3597
3552
|
}
|
|
3598
3553
|
};
|
|
3599
|
-
|
|
3600
3554
|
//#endregion
|
|
3601
3555
|
//#region src/parse/ParseWesl.ts
|
|
3602
3556
|
/** Parse a WESL source module into an AST. */
|
|
@@ -3646,7 +3600,6 @@ function createParseState(srcModule, options) {
|
|
|
3646
3600
|
state
|
|
3647
3601
|
};
|
|
3648
3602
|
}
|
|
3649
|
-
|
|
3650
3603
|
//#endregion
|
|
3651
3604
|
//#region src/ParseWESL.ts
|
|
3652
3605
|
/** Human-readable error when parsing WESL fails. */
|
|
@@ -3676,7 +3629,6 @@ function flatImports(ast, conditions) {
|
|
|
3676
3629
|
if (!conditions) ast._flatImports = flat;
|
|
3677
3630
|
return flat;
|
|
3678
3631
|
}
|
|
3679
|
-
|
|
3680
3632
|
//#endregion
|
|
3681
3633
|
//#region src/BindIdents.ts
|
|
3682
3634
|
/** Bind ref idents to declarations and mangle global declaration names. */
|
|
@@ -3940,7 +3892,6 @@ function collectDecls(items) {
|
|
|
3940
3892
|
return [];
|
|
3941
3893
|
});
|
|
3942
3894
|
}
|
|
3943
|
-
|
|
3944
3895
|
//#endregion
|
|
3945
3896
|
//#region src/PathUtil.ts
|
|
3946
3897
|
/** simplistic path manipulation utilities */
|
|
@@ -3963,7 +3914,6 @@ function noSuffix(path) {
|
|
|
3963
3914
|
const suffixStart = suffix === -1 ? path.length : suffix;
|
|
3964
3915
|
return path.slice(0, suffixStart);
|
|
3965
3916
|
}
|
|
3966
|
-
|
|
3967
3917
|
//#endregion
|
|
3968
3918
|
//#region src/ModuleResolver.ts
|
|
3969
3919
|
const libRegex = /^lib\.w[eg]sl$/i;
|
|
@@ -4071,6 +4021,24 @@ var BundleResolver = class {
|
|
|
4071
4021
|
return this.debugWeslRoot + this.packageName + "/" + filePath + ".wesl";
|
|
4072
4022
|
}
|
|
4073
4023
|
};
|
|
4024
|
+
/** Wrap a resolver so each resolveModule call returns a freshly parsed AST.
|
|
4025
|
+
* Use this when reusing a resolver across multiple link() calls, since binding
|
|
4026
|
+
* mutates ASTs in place. Each wrapper instance caches within itself, so within
|
|
4027
|
+
* a single link pass the same module path returns the same AST object.
|
|
4028
|
+
* Re-parsing is faster than structuredClone due to cycle-tracking overhead
|
|
4029
|
+
* from Scope.parent backpointers. */
|
|
4030
|
+
function freshResolver(inner) {
|
|
4031
|
+
const cache = /* @__PURE__ */ new Map();
|
|
4032
|
+
return { resolveModule(modulePath) {
|
|
4033
|
+
const cached = cache.get(modulePath);
|
|
4034
|
+
if (cached) return cached;
|
|
4035
|
+
const ast = inner.resolveModule(modulePath);
|
|
4036
|
+
if (!ast) return void 0;
|
|
4037
|
+
const fresh = parseSrcModule(ast.srcModule);
|
|
4038
|
+
cache.set(modulePath, fresh);
|
|
4039
|
+
return fresh;
|
|
4040
|
+
} };
|
|
4041
|
+
}
|
|
4074
4042
|
/** Convert file path to module path (e.g., "foo/bar.wesl" to "package::foo::bar"). */
|
|
4075
4043
|
function fileToModulePath(filePath, packageName, treatLibAsRoot) {
|
|
4076
4044
|
if (filePath.includes("::")) return filePath;
|
|
@@ -4089,7 +4057,6 @@ function findInVariants(sources, basePath, extensions = ["wesl", "wgsl"]) {
|
|
|
4089
4057
|
}
|
|
4090
4058
|
}
|
|
4091
4059
|
}
|
|
4092
|
-
|
|
4093
4060
|
//#endregion
|
|
4094
4061
|
//#region src/discovery/FindUnboundIdents.ts
|
|
4095
4062
|
/**
|
|
@@ -4168,7 +4135,6 @@ function discoverModules(weslSrc, resolver, rootModuleName, packageName = "packa
|
|
|
4168
4135
|
unbound
|
|
4169
4136
|
};
|
|
4170
4137
|
}
|
|
4171
|
-
|
|
4172
4138
|
//#endregion
|
|
4173
4139
|
//#region src/discovery/PackageNameUtils.ts
|
|
4174
4140
|
/** Package name sanitization for WESL.
|
|
@@ -4232,7 +4198,6 @@ function breakAt(str, delimiter) {
|
|
|
4232
4198
|
if (index === -1) return [str, ""];
|
|
4233
4199
|
return [str.slice(0, index), str.slice(index)];
|
|
4234
4200
|
}
|
|
4235
|
-
|
|
4236
4201
|
//#endregion
|
|
4237
4202
|
//#region src/LinkedWesl.ts
|
|
4238
4203
|
/**
|
|
@@ -4337,7 +4302,6 @@ function compilationInfoToErrorMessage(compilationInfo, shaderModule) {
|
|
|
4337
4302
|
}
|
|
4338
4303
|
return result;
|
|
4339
4304
|
}
|
|
4340
|
-
|
|
4341
4305
|
//#endregion
|
|
4342
4306
|
//#region src/SrcMap.ts
|
|
4343
4307
|
/** map text ranges in multiple src texts to a single dest text */
|
|
@@ -4488,7 +4452,6 @@ var SrcMapBuilder = class {
|
|
|
4488
4452
|
return map;
|
|
4489
4453
|
}
|
|
4490
4454
|
};
|
|
4491
|
-
|
|
4492
4455
|
//#endregion
|
|
4493
4456
|
//#region src/Linker.ts
|
|
4494
4457
|
/**
|
|
@@ -4584,7 +4547,7 @@ function normalizeModuleName(name) {
|
|
|
4584
4547
|
function getRootModule(resolver, modulePath, rootModuleName) {
|
|
4585
4548
|
const rootAst = resolver.resolveModule(modulePath);
|
|
4586
4549
|
if (!rootAst) {
|
|
4587
|
-
|
|
4550
|
+
console.log(`root module not found: ${modulePath} (from ${rootModuleName})`);
|
|
4588
4551
|
throw new Error(`Root module not found: ${rootModuleName}`);
|
|
4589
4552
|
}
|
|
4590
4553
|
return rootAst;
|
|
@@ -4660,7 +4623,6 @@ function emitDecl(decl, conditions) {
|
|
|
4660
4623
|
});
|
|
4661
4624
|
return builder;
|
|
4662
4625
|
}
|
|
4663
|
-
|
|
4664
4626
|
//#endregion
|
|
4665
4627
|
//#region src/LinkerUtil.ts
|
|
4666
4628
|
function visitAst(elem, visitor) {
|
|
@@ -4669,7 +4631,6 @@ function visitAst(elem, visitor) {
|
|
|
4669
4631
|
visitAst(child, visitor);
|
|
4670
4632
|
});
|
|
4671
4633
|
}
|
|
4672
|
-
|
|
4673
4634
|
//#endregion
|
|
4674
4635
|
//#region src/RawEmit.ts
|
|
4675
4636
|
function attributeToString$1(e) {
|
|
@@ -4717,13 +4678,11 @@ function contentsToString(elem) {
|
|
|
4717
4678
|
else if (elem.kind === "name") return elem.name;
|
|
4718
4679
|
else assertUnreachable(elem);
|
|
4719
4680
|
}
|
|
4720
|
-
|
|
4721
4681
|
//#endregion
|
|
4722
4682
|
//#region src/Reflection.ts
|
|
4723
4683
|
const textureStorage = matchOneOf(textureStorageTypes);
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4684
|
+
matchOneOf(sampledTextureTypes);
|
|
4685
|
+
matchOneOf(multisampledTextureTypes);
|
|
4727
4686
|
//#endregion
|
|
4728
4687
|
//#region src/TransformBindingStructs.ts
|
|
4729
4688
|
function bindingStructsPlugin() {
|
|
@@ -4907,7 +4866,7 @@ function transformBindingReference(memberRef, struct) {
|
|
|
4907
4866
|
const refName = memberRef.member.name;
|
|
4908
4867
|
const structMember = struct.members.find((m) => m.name.name === refName);
|
|
4909
4868
|
if (!structMember || !structMember.mangledVarName) {
|
|
4910
|
-
|
|
4869
|
+
console.log(`missing mangledVarName for ${refName}`);
|
|
4911
4870
|
return {
|
|
4912
4871
|
kind: "synthetic",
|
|
4913
4872
|
text: refName
|
|
@@ -4922,7 +4881,6 @@ function transformBindingReference(memberRef, struct) {
|
|
|
4922
4881
|
memberRef.contents = [synthElem];
|
|
4923
4882
|
return synthElem;
|
|
4924
4883
|
}
|
|
4925
|
-
|
|
4926
4884
|
//#endregion
|
|
4927
4885
|
//#region src/WeslDevice.ts
|
|
4928
4886
|
/**
|
|
@@ -4990,6 +4948,5 @@ function makeWeslDevice(device) {
|
|
|
4990
4948
|
})(device.popErrorScope);
|
|
4991
4949
|
return device;
|
|
4992
4950
|
}
|
|
4993
|
-
|
|
4994
4951
|
//#endregion
|
|
4995
|
-
export { BundleResolver, CompositeResolver, LinkedWesl, ParseError, RecordResolver, SrcMap, SrcMapBuilder, TrackingResolver, WeslParseError, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, childIdent, childScope, containsScope, debug, debugContentsToString, discoverModules, emptyScope, errorHighlight, fileToModulePath, filterMap, filterValidElements, findAllRootDecls, findMap, findRefsToBindingStructs, findUnboundIdents, findUnboundRefs, findValidRootDecls, flatImports, groupBy, grouped, identToString, last, lengthPrefixMangle, link, linkRegistry, liveDeclsToString, log, lowerBindingStructs, makeLiveDecls, makeWeslDevice, mapForward, mapValues, markBindingStructs, markEntryTypes, mergeScope, minimalMangle, minimallyMangledName, modulePartsToRelativePath, moduleToRelativePath, multiKeySet, multisampledTextureTypes, nextIdentId, noSuffix, normalize, normalizeDebugRoot, normalizeModuleName, npmNameVariations, offsetToLineNumber, overlapTail, parseSrcModule, partition, publicDecl, replaceWords, requestWeslDevice, resetScopeIds, resolveModulePath, sampledTextureTypes, sanitizePackageName, scan, scopeToString, scopeToStringLong, srcLog, stdEnumerant, stdEnumerants, stdFn, stdFns, stdType, stdTypes, textureStorageTypes, transformBindingReference, transformBindingStruct, underscoreMangle, validation, wgslStandardAttributes, withLoggerAsync };
|
|
4952
|
+
export { BundleResolver, CompositeResolver, LinkedWesl, ParseError, RecordResolver, SrcMap, SrcMapBuilder, TrackingResolver, WeslParseError, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, childIdent, childScope, containsScope, debug, debugContentsToString, discoverModules, emptyScope, errorHighlight, fileToModulePath, filterMap, filterValidElements, findAllRootDecls, findMap, findRefsToBindingStructs, findUnboundIdents, findUnboundRefs, findValidRootDecls, flatImports, freshResolver, groupBy, grouped, identToString, last, lengthPrefixMangle, link, linkRegistry, liveDeclsToString, log, lowerBindingStructs, makeLiveDecls, makeWeslDevice, mapForward, mapValues, markBindingStructs, markEntryTypes, mergeScope, minimalMangle, minimallyMangledName, modulePartsToRelativePath, moduleToRelativePath, multiKeySet, multisampledTextureTypes, nextIdentId, noSuffix, normalize, normalizeDebugRoot, normalizeModuleName, npmNameVariations, offsetToLineNumber, overlapTail, parseSrcModule, partition, publicDecl, replaceWords, requestWeslDevice, resetScopeIds, resolveModulePath, sampledTextureTypes, sanitizePackageName, scan, scopeToString, scopeToStringLong, srcLog, stdEnumerant, stdEnumerants, stdFn, stdFns, stdType, stdTypes, textureStorageTypes, transformBindingReference, transformBindingStruct, underscoreMangle, validation, wgslStandardAttributes, withLoggerAsync };
|
package/package.json
CHANGED
package/src/ModuleResolver.ts
CHANGED
|
@@ -157,6 +157,27 @@ export class BundleResolver implements ModuleResolver {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/** Wrap a resolver so each resolveModule call returns a freshly parsed AST.
|
|
161
|
+
* Use this when reusing a resolver across multiple link() calls, since binding
|
|
162
|
+
* mutates ASTs in place. Each wrapper instance caches within itself, so within
|
|
163
|
+
* a single link pass the same module path returns the same AST object.
|
|
164
|
+
* Re-parsing is faster than structuredClone due to cycle-tracking overhead
|
|
165
|
+
* from Scope.parent backpointers. */
|
|
166
|
+
export function freshResolver(inner: ModuleResolver): ModuleResolver {
|
|
167
|
+
const cache = new Map<string, WeslAST>();
|
|
168
|
+
return {
|
|
169
|
+
resolveModule(modulePath: string): WeslAST | undefined {
|
|
170
|
+
const cached = cache.get(modulePath);
|
|
171
|
+
if (cached) return cached;
|
|
172
|
+
const ast = inner.resolveModule(modulePath);
|
|
173
|
+
if (!ast) return undefined;
|
|
174
|
+
const fresh = parseSrcModule(ast.srcModule);
|
|
175
|
+
cache.set(modulePath, fresh);
|
|
176
|
+
return fresh;
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
160
181
|
/** Convert file path to module path (e.g., "foo/bar.wesl" to "package::foo::bar"). */
|
|
161
182
|
export function fileToModulePath(
|
|
162
183
|
filePath: string,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { link } from "../Linker.ts";
|
|
3
|
+
import { freshResolver, RecordResolver } from "../ModuleResolver.ts";
|
|
4
|
+
|
|
5
|
+
test("link twice with same resolver via freshResolver", async () => {
|
|
6
|
+
const weslSrc = {
|
|
7
|
+
"main.wesl": `
|
|
8
|
+
@if(A) fn foo() -> i32 { return 1; }
|
|
9
|
+
@else fn foo() -> i32 { return 2; }
|
|
10
|
+
`,
|
|
11
|
+
};
|
|
12
|
+
const resolver = new RecordResolver(weslSrc);
|
|
13
|
+
|
|
14
|
+
const [r1, r2] = await Promise.all([
|
|
15
|
+
link({
|
|
16
|
+
resolver: freshResolver(resolver),
|
|
17
|
+
rootModuleName: "main",
|
|
18
|
+
conditions: { A: true },
|
|
19
|
+
}),
|
|
20
|
+
link({
|
|
21
|
+
resolver: freshResolver(resolver),
|
|
22
|
+
rootModuleName: "main",
|
|
23
|
+
conditions: { A: false },
|
|
24
|
+
}),
|
|
25
|
+
]);
|
|
26
|
+
expect(r1.dest).toContain("return 1");
|
|
27
|
+
expect(r2.dest).toContain("return 2");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("freshResolver returns same AST within one instance", () => {
|
|
31
|
+
const resolver = new RecordResolver({ main: `fn foo() {}` });
|
|
32
|
+
const fresh = freshResolver(resolver);
|
|
33
|
+
const ast1 = fresh.resolveModule("package::main");
|
|
34
|
+
const ast2 = fresh.resolveModule("package::main");
|
|
35
|
+
expect(ast1).toBe(ast2);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("freshResolver returns different AST across instances", () => {
|
|
39
|
+
const resolver = new RecordResolver({ main: `fn foo() {}` });
|
|
40
|
+
const ast1 = freshResolver(resolver).resolveModule("package::main");
|
|
41
|
+
const ast2 = freshResolver(resolver).resolveModule("package::main");
|
|
42
|
+
expect(ast1).not.toBe(ast2);
|
|
43
|
+
});
|
package/src/test/TestLink.ts
CHANGED
|
@@ -69,7 +69,8 @@ export async function testFromCase(
|
|
|
69
69
|
|
|
70
70
|
/** For afterAll(): verify all shared test suite cases are covered. */
|
|
71
71
|
export function verifyCaseCoverage(caseList: WgslTestSrc[]) {
|
|
72
|
-
|
|
72
|
+
// biome-ignore lint/correctness/noEmptyPattern: vitest 4 requires destructuring for 1st arg
|
|
73
|
+
return ({}: object, suite: RunnerTestSuite) => {
|
|
73
74
|
const testNames = new Set(suite.tasks.map(t => t.name));
|
|
74
75
|
const missing = caseList
|
|
75
76
|
.map(c => c.name)
|