wesl 0.7.23 → 0.7.25

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 CHANGED
@@ -41,5 +41,5 @@ fn random_color(uv: vec2u) -> vec3f {
41
41
  </pre>
42
42
 
43
43
  [wesl plugin]: https://www.npmjs.com/package/wesl-plugin
44
- [examples]: https://github.com/wgsl-tooling-wg/wesl-js/tree/main/tools/examples
44
+ [examples]: https://github.com/wgsl-tooling-wg/wesl-js/tree/main/examples
45
45
  [wesl]: https://www.npmjs.com/package/wesl
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
@@ -880,6 +887,8 @@ interface LinkParams {
880
887
  /** function to construct globally unique wgsl identifiers */
881
888
  mangler?: ManglerFn;
882
889
  }
890
+ /** Project config for web components and tools. */
891
+ type WeslProject = Pick<LinkParams, "weslSrc" | "rootModuleName" | "conditions" | "constants" | "libs" | "packageName">;
883
892
  /** Generate a virtual WESL module based on a set of conditions. */
884
893
  type VirtualLibraryFn = (conditions: Conditions) => string;
885
894
  /**
@@ -1275,4 +1284,4 @@ declare function offsetToLineNumber(offset: number, text: string): [lineNum: num
1275
1284
  */
1276
1285
  declare function errorHighlight(source: string, span: Span): [string, string];
1277
1286
  //#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 };
1287
+ 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, WeslProject, 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
- if (debug) assertThat(condition, msg);
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 (!debug || !isBrowser) throw error;
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
- if (debug) e.cause = error;
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
- if (debug) console.log(`root module not found: ${modulePath} (from ${rootModuleName})`);
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
- const textureTypes = matchOneOf(sampledTextureTypes);
4725
- const multiNames = matchOneOf(multisampledTextureTypes);
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
- if (debug) console.log(`missing mangledVarName for ${refName}`);
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 };
@@ -1,5 +1,4 @@
1
1
  //#region src/weslBundleDeclUrl.ts
2
2
  const weslBundleDeclUrl = new URL("../src/WeslBundle.ts", import.meta.url).href;
3
-
4
3
  //#endregion
5
- export { weslBundleDeclUrl };
4
+ export { weslBundleDeclUrl };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wesl",
3
- "version": "0.7.23",
3
+ "version": "0.7.25",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -23,8 +23,7 @@
23
23
  "multi_pkg": "x",
24
24
  "terser": "^5.43.1",
25
25
  "typedoc": "^0.28.7",
26
- "typedoc-theme-hierarchy": "^6.0.0",
27
- "wrangler": "^4.22.0"
26
+ "typedoc-theme-hierarchy": "^6.0.0"
28
27
  },
29
28
  "license": "MIT",
30
29
  "keywords": [
@@ -38,7 +37,7 @@
38
37
  "build:nodebug": "vite build --config vite.nodebug.config.ts",
39
38
  "build:size": "./scripts/size-check.ts",
40
39
  "deploy:docsite": "run-s build:docs pages:deploy",
41
- "pages:deploy": "wrangler pages deploy --project-name wesl-js docs",
40
+ "pages:deploy": "pnpx wrangler pages deploy --project-name wesl-js docs",
42
41
  "test": "vitest --hideSkippedTests",
43
42
  "test:cts": "./scripts/test-cts.ts",
44
43
  "test:nodebug": "vitest run --config vitest.nodebug.config.ts",
package/src/Linker.ts CHANGED
@@ -91,6 +91,17 @@ export interface LinkParams {
91
91
  mangler?: ManglerFn;
92
92
  }
93
93
 
94
+ /** Project config for web components and tools. */
95
+ export type WeslProject = Pick<
96
+ LinkParams,
97
+ | "weslSrc"
98
+ | "rootModuleName"
99
+ | "conditions"
100
+ | "constants"
101
+ | "libs"
102
+ | "packageName"
103
+ >;
104
+
94
105
  /** Generate a virtual WESL module based on a set of conditions. */
95
106
  export type VirtualLibraryFn = (conditions: Conditions) => string;
96
107
 
@@ -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
+ });
@@ -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
- return (suite: RunnerTestSuite) => {
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)