wesl 0.7.20 → 0.7.22

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 CHANGED
@@ -725,9 +725,13 @@ declare class LinkedWesl {
725
725
  * . lengthPrefixMangle constructs long but predictable names for every declaration
726
726
  */
727
727
  type ManglerFn = (/** global declaration that needs a name */
728
+
728
729
  decl: DeclIdent, /** module that contains the declaration */
730
+
729
731
  srcModule: SrcModule, /** name at use site (possibly import as renamed from declaration) */
732
+
730
733
  proposedName: string, /** current set of mangled root level names for the linked result (read only) */
734
+
731
735
  globalNames: Set<string>) => string;
732
736
  /**
733
737
  * Construct a globally unique name based on the declaration
@@ -977,6 +981,8 @@ interface BindIdentsParams extends Pick<LinkRegistryParams, "resolver" | "condit
977
981
  declare function bindIdents(params: BindIdentsParams): BindResults;
978
982
  /** Find all conditionally valid declarations at the root level. */
979
983
  declare function findValidRootDecls(rootScope: Scope, conditions: Conditions): DeclIdent[];
984
+ /** Find all declarations at the root level, ignoring conditions. */
985
+ declare function findAllRootDecls(rootScope: Scope): DeclIdent[];
980
986
  /** Find a public declaration with the given original name. */
981
987
  declare function publicDecl(scope: Scope, name: string, conditions: Conditions): DeclIdent | undefined;
982
988
  /** State used during the recursive scope tree walk to bind references to declarations. */
@@ -1000,6 +1006,8 @@ interface BindContext {
1000
1006
  unbound?: UnboundRef[];
1001
1007
  /** Don't follow references from declarations (for library dependency detection). */
1002
1008
  dontFollowDecls?: boolean;
1009
+ /** Visit all conditional branches (for dependency discovery). */
1010
+ discoveryMode?: boolean;
1003
1011
  }
1004
1012
  /**
1005
1013
  * Recursively bind references to declarations in this scope and child scopes.
@@ -1233,4 +1241,4 @@ declare function offsetToLineNumber(offset: number, text: string): [lineNum: num
1233
1241
  */
1234
1242
  declare function errorHighlight(source: string, span: Span): [string, string];
1235
1243
  //#endregion
1236
- 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, SimpleMemberRef, Span, SrcMap, SrcMapBuilder, SrcMapEntry, SrcModule, SrcPosition, SrcWithPath, StableState, StandardAttribute, StatementElem, StructElem, StructMemberElem, StuffElem, SwitchClauseElem, SyntheticElem, TerminalElem, TextElem, 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, emptyScope, errorHighlight, fileToModulePath, filterMap, 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, withLoggerAsync };
1244
+ 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, SimpleMemberRef, Span, SrcMap, SrcMapBuilder, SrcMapEntry, SrcModule, SrcPosition, SrcWithPath, StableState, StandardAttribute, StatementElem, StructElem, StructMemberElem, StuffElem, SwitchClauseElem, SyntheticElem, TerminalElem, TextElem, 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, emptyScope, errorHighlight, fileToModulePath, filterMap, 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, withLoggerAsync };
package/dist/index.js CHANGED
@@ -334,8 +334,8 @@ function throwClickableError(params) {
334
334
  }
335
335
  }
336
336
  /** Warn the user about an identifier and throw a clickable exception */
337
- function failIdent(ident$1, msg) {
338
- const { refIdentElem, originalName } = ident$1;
337
+ function failIdent(ident, msg) {
338
+ const { refIdentElem, originalName } = ident;
339
339
  const baseMessage = msg ?? `'${originalName}'`;
340
340
  if (refIdentElem) failIdentElem(refIdentElem, baseMessage);
341
341
  else throw new Error(baseMessage);
@@ -717,13 +717,13 @@ function emitContentsWithTrimming(elem, ctx) {
717
717
  const validElements = filterValidElements(elem.contents, ctx.conditions);
718
718
  const firstEmit = validElements.findIndex((e) => !isConditionalAttr(e));
719
719
  const lastEmit = validElements.findLastIndex((e) => !isConditionalAttr(e));
720
- validElements.forEach((elem$1, i) => {
721
- if (elem$1.kind === "text") {
722
- let text = elem$1.srcModule.src.slice(elem$1.start, elem$1.end);
720
+ validElements.forEach((elem, i) => {
721
+ if (elem.kind === "text") {
722
+ let text = elem.srcModule.src.slice(elem.start, elem.end);
723
723
  if (i === firstEmit) text = text.trimStart();
724
724
  if (i === lastEmit) text = text.trimEnd();
725
- if (text) ctx.srcBuilder.add(text, elem$1.start, elem$1.end);
726
- } else lowerAndEmitElem(elem$1, ctx);
725
+ if (text) ctx.srcBuilder.add(text, elem.start, elem.end);
726
+ } else lowerAndEmitElem(elem, ctx);
727
727
  });
728
728
  }
729
729
  function isConditionalAttr(e) {
@@ -885,13 +885,13 @@ function displayName(declIdent) {
885
885
  return declIdent.mangledName || declIdent.originalName;
886
886
  }
887
887
  /** Trace through refersTo links until we find the declaration. */
888
- function findDecl(ident$1) {
889
- let i = ident$1;
888
+ function findDecl(ident) {
889
+ let i = ident;
890
890
  do {
891
891
  if (i.kind === "decl") return i;
892
892
  i = i.refersTo;
893
893
  } while (i);
894
- throw new Error(`unresolved identifer: ${ident$1.originalName}`);
894
+ throw new Error(`unresolved identifer: ${ident.originalName}`);
895
895
  }
896
896
 
897
897
  //#endregion
@@ -1122,7 +1122,7 @@ function scopeToString(scope, indent = 0, shortIdents = true) {
1122
1122
  if (attrStrings) str.add(attrStrings + " ");
1123
1123
  if (kind === "partial") str.add("-");
1124
1124
  str.add("{ ");
1125
- const last$1 = contents.length - 1;
1125
+ const last = contents.length - 1;
1126
1126
  let lastWasScope = false;
1127
1127
  let hasBlock = false;
1128
1128
  contents.forEach((elem, i) => {
@@ -1135,10 +1135,10 @@ function scopeToString(scope, indent = 0, shortIdents = true) {
1135
1135
  } else {
1136
1136
  if (lastWasScope) str.add(" ");
1137
1137
  lastWasScope = false;
1138
- const ident$1 = elem;
1139
- if (shortIdents) str.add(identShortString(ident$1));
1140
- else str.add(identToString(ident$1));
1141
- if (i < last$1) str.add(" ");
1138
+ const ident = elem;
1139
+ if (shortIdents) str.add(identShortString(ident));
1140
+ else str.add(identToString(ident));
1141
+ if (i < last) str.add(" ");
1142
1142
  }
1143
1143
  });
1144
1144
  if (!hasBlock && str.oneLine) str.add(" }");
@@ -1154,17 +1154,17 @@ function scopeToStringLong(scope) {
1154
1154
  return scopeToString(scope, 0, false);
1155
1155
  }
1156
1156
  /** name of an identifier, with decls prefixed with '%' */
1157
- function identShortString(ident$1) {
1158
- const { kind, originalName } = ident$1;
1157
+ function identShortString(ident) {
1158
+ const { kind, originalName } = ident;
1159
1159
  return `${kind === "decl" ? "%" : ""}${originalName}`;
1160
1160
  }
1161
- function identToString(ident$1) {
1162
- if (!ident$1) return JSON.stringify(ident$1);
1163
- const { kind, originalName } = ident$1;
1164
- const idStr = ident$1.id ? `#${ident$1.id}` : "";
1165
- if (kind === "ref") return `${originalName} ${idStr} -> ${identToString(ident$1.refersTo)}`;
1161
+ function identToString(ident) {
1162
+ if (!ident) return JSON.stringify(ident);
1163
+ const { kind, originalName } = ident;
1164
+ const idStr = ident.id ? `#${ident.id}` : "";
1165
+ if (kind === "ref") return `${originalName} ${idStr} -> ${identToString(ident.refersTo)}`;
1166
1166
  else {
1167
- const { mangledName } = ident$1;
1167
+ const { mangledName } = ident;
1168
1168
  return `%${originalName}${mangledName ? `(${mangledName})` : ""} ${idStr} `;
1169
1169
  }
1170
1170
  }
@@ -1655,11 +1655,11 @@ function parseIdent(ctx, conditionRef) {
1655
1655
  const path = parseModulePath(ctx.stream);
1656
1656
  if (!path) return null;
1657
1657
  const { parts, start, end } = path;
1658
- const ident$1 = ctx.createRefIdent(parts.join("::"));
1659
- if (conditionRef) ident$1.conditionRef = true;
1660
- const refIdentElem = makeRefIdentElem(ctx, ident$1, start, end);
1658
+ const ident = ctx.createRefIdent(parts.join("::"));
1659
+ if (conditionRef) ident.conditionRef = true;
1660
+ const refIdentElem = makeRefIdentElem(ctx, ident, start, end);
1661
1661
  if (!conditionRef) {
1662
- ctx.saveIdent(ident$1);
1662
+ ctx.saveIdent(ident);
1663
1663
  ctx.addElem(refIdentElem);
1664
1664
  }
1665
1665
  return refIdentElem;
@@ -2202,8 +2202,8 @@ function parseElseChain(ctx) {
2202
2202
  while (stream.matchText("else")) {
2203
2203
  if (stream.matchText("if")) {
2204
2204
  expectExpression(ctx, "Expected expression after 'else if'");
2205
- const body$1 = expectCompound(ctx, "Expected '{' after else if");
2206
- ctx.addElem(body$1);
2205
+ const body = expectCompound(ctx, "Expected '{' after else if");
2206
+ ctx.addElem(body);
2207
2207
  continue;
2208
2208
  }
2209
2209
  const body = expectCompound(ctx, "Expected '{' after else");
@@ -3162,8 +3162,8 @@ var ParsingContext = class {
3162
3162
  declElem: null
3163
3163
  };
3164
3164
  }
3165
- saveIdent(ident$1) {
3166
- this.state.context.scope.contents.push(ident$1);
3165
+ saveIdent(ident) {
3166
+ this.state.context.scope.contents.push(ident);
3167
3167
  }
3168
3168
  };
3169
3169
 
@@ -3252,7 +3252,7 @@ function toRegexSource(nameExp) {
3252
3252
  }
3253
3253
  }
3254
3254
  function verifyNonCapturing(name, exp) {
3255
- if ((/* @__PURE__ */ new RegExp("|" + exp.source)).exec("").length > 1) throw new Error(`match expression groups must be non-capturing: ${name}: /${exp.source}/. Use (?:...) instead.`);
3255
+ if (new RegExp("|" + exp.source).exec("").length > 1) throw new Error(`match expression groups must be non-capturing: ${name}: /${exp.source}/. Use (?:...) instead.`);
3256
3256
  }
3257
3257
  const regexSpecials = /[$+*.?|(){}[\]\\/^]/g;
3258
3258
  function escapeRegex(s) {
@@ -3761,6 +3761,13 @@ function findValidRootDecls(rootScope, conditions) {
3761
3761
  else if (item.kind === "partial") collectDecls(item, found);
3762
3762
  return found;
3763
3763
  }
3764
+ /** Find all declarations at the root level, ignoring conditions. */
3765
+ function findAllRootDecls(rootScope) {
3766
+ const found = [];
3767
+ for (const item of rootScope.contents) if (item.kind === "decl") found.push(item);
3768
+ else if (item.kind === "partial") collectDecls(item, found);
3769
+ return found;
3770
+ }
3764
3771
  /** Find a public declaration with the given original name. */
3765
3772
  function publicDecl(scope, name, conditions) {
3766
3773
  return getValidRootDecls(scope, conditions).find((d) => d.originalName === name);
@@ -3785,7 +3792,8 @@ function bindIdentsRecursive(scope, bindContext, liveDecls) {
3785
3792
  function processScope(scope, bindContext, liveDecls) {
3786
3793
  const newGlobals = [];
3787
3794
  const newFromChildren = [];
3788
- for (const child of validItems(scope, bindContext.conditions)) if (child.kind === "decl") liveDecls.decls.set(child.originalName, child);
3795
+ const items = bindContext.discoveryMode ? scope.contents : validItems(scope, bindContext.conditions);
3796
+ for (const child of items) if (child.kind === "decl") liveDecls.decls.set(child.originalName, child);
3789
3797
  else if (child.kind === "ref") {
3790
3798
  const newDecl = handleRef(child, liveDecls, bindContext);
3791
3799
  if (newDecl) newGlobals.push(newDecl);
@@ -3811,20 +3819,20 @@ function processDependentScope(decl, ctx) {
3811
3819
  * Mutates to mangle declarations and mark std references.
3812
3820
  * @return found declaration, or undefined if already processed
3813
3821
  */
3814
- function handleRef(ident$1, liveDecls, bindContext) {
3815
- if (ident$1.refersTo || ident$1.std) return;
3816
- if (ident$1.conditionRef) return;
3817
- if (ident$1.attrParam && !wgslStandardAttributes.has(ident$1.attrParam)) return;
3818
- const foundDecl = findDeclInModule(ident$1, liveDecls) ?? findQualifiedImport(ident$1, bindContext);
3822
+ function handleRef(ident, liveDecls, bindContext) {
3823
+ if (ident.refersTo || ident.std) return;
3824
+ if (ident.conditionRef) return;
3825
+ if (ident.attrParam && !wgslStandardAttributes.has(ident.attrParam)) return;
3826
+ const foundDecl = findDeclInModule(ident, liveDecls) ?? findQualifiedImport(ident, bindContext);
3819
3827
  if (foundDecl) {
3820
- ident$1.refersTo = foundDecl.decl;
3821
- return handleNewDecl(ident$1, foundDecl, bindContext);
3828
+ ident.refersTo = foundDecl.decl;
3829
+ return handleNewDecl(ident, foundDecl, bindContext);
3822
3830
  }
3823
- if (stdWgsl(ident$1.originalName)) {
3824
- ident$1.std = true;
3831
+ if (stdWgsl(ident.originalName)) {
3832
+ ident.std = true;
3825
3833
  return;
3826
3834
  }
3827
- if (!bindContext.unbound) failIdent(ident$1, `unresolved identifier '${ident$1.originalName}'`);
3835
+ if (!bindContext.unbound) failIdent(ident, `unresolved identifier '${ident.originalName}'`);
3828
3836
  }
3829
3837
  function handleDecls(newGlobals, bindContext) {
3830
3838
  return newGlobals.flatMap((decl) => processDependentScope(decl, bindContext));
@@ -3845,18 +3853,18 @@ function handleNewDecl(refIdent, foundDecl, ctx) {
3845
3853
  return decl;
3846
3854
  }
3847
3855
  /** Search current scope and parent scopes for a matching declaration. */
3848
- function findDeclInModule(ident$1, liveDecls) {
3849
- const found = liveDecls.decls.get(ident$1.originalName);
3856
+ function findDeclInModule(ident, liveDecls) {
3857
+ const found = liveDecls.decls.get(ident.originalName);
3850
3858
  if (found) return {
3851
3859
  decl: found,
3852
- moduleAst: ident$1.ast
3860
+ moduleAst: ident.ast
3853
3861
  };
3854
- if (liveDecls.parent) return findDeclInModule(ident$1, liveDecls.parent);
3862
+ if (liveDecls.parent) return findDeclInModule(ident, liveDecls.parent);
3855
3863
  }
3856
3864
  /** Match a ref ident to a declaration in another module via import or qualified ident. */
3857
3865
  function findQualifiedImport(refIdent, ctx) {
3858
- const { conditions, unbound } = ctx;
3859
- const flatImps = flatImports(refIdent.ast, conditions);
3866
+ const { conditions, unbound, discoveryMode } = ctx;
3867
+ const flatImps = flatImports(refIdent.ast, discoveryMode ? void 0 : conditions);
3860
3868
  const identParts = refIdent.originalName.split("::");
3861
3869
  const pathParts = matchingImport(identParts, flatImps) ?? qualifiedIdent(identParts);
3862
3870
  if (!pathParts) {
@@ -3975,10 +3983,11 @@ function findUnboundRefs(resolver) {
3975
3983
  mangler: minimalMangle,
3976
3984
  packageName: "package",
3977
3985
  unbound: [],
3978
- dontFollowDecls: true
3986
+ dontFollowDecls: true,
3987
+ discoveryMode: true
3979
3988
  };
3980
3989
  for (const [, ast] of resolver.allModules()) {
3981
- const rootDecls = findValidRootDecls(ast.rootScope, {});
3990
+ const rootDecls = findAllRootDecls(ast.rootScope);
3982
3991
  const liveDecls = {
3983
3992
  decls: new Map(rootDecls.map((d) => [d.originalName, d])),
3984
3993
  parent: null
@@ -4857,8 +4866,8 @@ function refersToBindingStruct(memberRef) {
4857
4866
  };
4858
4867
  }
4859
4868
  /** If this identifier ultimately refers to a struct type, return the struct declaration */
4860
- function traceToStruct(ident$1) {
4861
- const declElem = findDecl(ident$1).declElem;
4869
+ function traceToStruct(ident) {
4870
+ const declElem = findDecl(ident).declElem;
4862
4871
  if (declElem && declElem.kind === "param") {
4863
4872
  const name = declElem.name.typeRef?.name;
4864
4873
  if (typeof name !== "string") {
@@ -4963,4 +4972,4 @@ function makeWeslDevice(device) {
4963
4972
  }
4964
4973
 
4965
4974
  //#endregion
4966
- export { BundleResolver, CompositeResolver, LinkedWesl, ParseError, RecordResolver, SrcMap, SrcMapBuilder, WeslParseError, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, childIdent, childScope, containsScope, debug, debugContentsToString, emptyScope, errorHighlight, fileToModulePath, filterMap, 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, withLoggerAsync };
4975
+ export { BundleResolver, CompositeResolver, LinkedWesl, ParseError, RecordResolver, SrcMap, SrcMapBuilder, WeslParseError, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, childIdent, childScope, containsScope, debug, debugContentsToString, emptyScope, errorHighlight, fileToModulePath, filterMap, 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, withLoggerAsync };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wesl",
3
- "version": "0.7.20",
3
+ "version": "0.7.22",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -26,14 +26,6 @@
26
26
  "typedoc-theme-hierarchy": "^6.0.0",
27
27
  "wrangler": "^4.22.0"
28
28
  },
29
- "peerDependencies": {
30
- "random_wgsl": "^0.6.68"
31
- },
32
- "peerDependenciesMeta": {
33
- "random_wgsl": {
34
- "optional": true
35
- }
36
- },
37
29
  "license": "MIT",
38
30
  "keywords": [
39
31
  "webgpu",
package/src/BindIdents.ts CHANGED
@@ -203,6 +203,16 @@ export function findValidRootDecls(
203
203
  return found;
204
204
  }
205
205
 
206
+ /** Find all declarations at the root level, ignoring conditions. */
207
+ export function findAllRootDecls(rootScope: Scope): DeclIdent[] {
208
+ const found: DeclIdent[] = [];
209
+ for (const item of rootScope.contents) {
210
+ if (item.kind === "decl") found.push(item);
211
+ else if (item.kind === "partial") collectDecls(item, found);
212
+ }
213
+ return found;
214
+ }
215
+
206
216
  /** Find a public declaration with the given original name. */
207
217
  export function publicDecl(
208
218
  scope: Scope,
@@ -244,6 +254,9 @@ interface BindContext {
244
254
 
245
255
  /** Don't follow references from declarations (for library dependency detection). */
246
256
  dontFollowDecls?: boolean;
257
+
258
+ /** Visit all conditional branches (for dependency discovery). */
259
+ discoveryMode?: boolean;
247
260
  }
248
261
 
249
262
  /**
@@ -277,7 +290,10 @@ function processScope(
277
290
  const newGlobals: DeclIdent[] = [];
278
291
  const newFromChildren: DeclIdent[] = [];
279
292
 
280
- for (const child of validItems(scope, bindContext.conditions)) {
293
+ const items = bindContext.discoveryMode
294
+ ? scope.contents
295
+ : validItems(scope, bindContext.conditions);
296
+ for (const child of items) {
281
297
  if (child.kind === "decl") {
282
298
  liveDecls.decls.set(child.originalName, child);
283
299
  } else if (child.kind === "ref") {
@@ -380,8 +396,11 @@ function findQualifiedImport(
380
396
  refIdent: RefIdent,
381
397
  ctx: BindContext,
382
398
  ): FoundDecl | undefined {
383
- const { conditions, unbound } = ctx;
384
- const flatImps = flatImports(refIdent.ast, conditions);
399
+ const { conditions, unbound, discoveryMode } = ctx;
400
+ const flatImps = flatImports(
401
+ refIdent.ast,
402
+ discoveryMode ? undefined : conditions,
403
+ );
385
404
  const identParts = refIdent.originalName.split("::");
386
405
  const pathParts =
387
406
  matchingImport(identParts, flatImps) ?? qualifiedIdent(identParts);
@@ -2,7 +2,7 @@ import type { AbstractElem } from "../AbstractElems.ts";
2
2
  import {
3
3
  bindIdentsRecursive,
4
4
  type EmittableElem,
5
- findValidRootDecls,
5
+ findAllRootDecls,
6
6
  type UnboundRef,
7
7
  } from "../BindIdents.ts";
8
8
  import { type LiveDecls, makeLiveDecls } from "../LiveDeclarations.ts";
@@ -38,10 +38,11 @@ export function findUnboundRefs(resolver: BatchModuleResolver): UnboundRef[] {
38
38
  packageName: "package",
39
39
  unbound: [] as UnboundRef[],
40
40
  dontFollowDecls: true,
41
+ discoveryMode: true,
41
42
  };
42
43
 
43
44
  for (const [, ast] of resolver.allModules()) {
44
- const rootDecls = findValidRootDecls(ast.rootScope, {});
45
+ const rootDecls = findAllRootDecls(ast.rootScope);
45
46
  const liveDecls: LiveDecls = {
46
47
  decls: new Map(rootDecls.map(d => [d.originalName, d] as const)),
47
48
  parent: null,
@@ -0,0 +1,71 @@
1
+ import { expect, test } from "vitest";
2
+ import { findUnboundIdents } from "../discovery/FindUnboundIdents.ts";
3
+ import { RecordResolver } from "../ModuleResolver.ts";
4
+
5
+ test("ref inside @if block is discovered", () => {
6
+ const srcs = {
7
+ "./test.wesl": `
8
+ import package::util::helper;
9
+ fn main() {
10
+ @if(feature) helper();
11
+ }
12
+ `,
13
+ "./util.wesl": `
14
+ import ext_pkg::dep;
15
+ fn helper() { dep(); }
16
+ `,
17
+ };
18
+ const resolver = new RecordResolver(srcs);
19
+ const unbound = findUnboundIdents(resolver);
20
+ expect(unbound).toContainEqual(["ext_pkg", "dep"]);
21
+ });
22
+
23
+ test("ref inside @else block is discovered", () => {
24
+ const srcs = {
25
+ "./test.wesl": `
26
+ import package::util::a;
27
+ import package::util::b;
28
+ fn main() {
29
+ @if(feature) a();
30
+ @else b();
31
+ }
32
+ `,
33
+ "./util.wesl": `
34
+ import ext_pkg::dep;
35
+ fn a() { }
36
+ fn b() { dep(); }
37
+ `,
38
+ };
39
+ const resolver = new RecordResolver(srcs);
40
+ const unbound = findUnboundIdents(resolver);
41
+ expect(unbound).toContainEqual(["ext_pkg", "dep"]);
42
+ });
43
+
44
+ test("import declaration inside @if block is discovered", () => {
45
+ const srcs = {
46
+ "./test.wesl": `
47
+ @if(feature)
48
+ import ext_pkg::mod::helper;
49
+
50
+ fn main() {
51
+ @if(feature) helper();
52
+ }
53
+ `,
54
+ };
55
+ const resolver = new RecordResolver(srcs);
56
+ const unbound = findUnboundIdents(resolver);
57
+ expect(unbound).toContainEqual(["ext_pkg", "mod", "helper"]);
58
+ });
59
+
60
+ test("inline qualified ref inside @if is discovered", () => {
61
+ const srcs = {
62
+ "./test.wesl": `
63
+ fn main() {
64
+ @if(feature) ext_pkg::mod::helper();
65
+ }
66
+ `,
67
+ };
68
+ const resolver = new RecordResolver(srcs);
69
+ const unbound = findUnboundIdents(resolver);
70
+ expect(unbound).toContainEqual(["ext_pkg", "mod", "helper"]);
71
+ });
@@ -1,30 +1,36 @@
1
1
  import second from "multi_pkg/second";
2
2
  import trans from "multi_pkg/transitive";
3
- import rand from "random_wgsl";
4
3
  import { expect, test } from "vitest";
5
4
  import { link } from "../Linker.ts";
5
+ import type { WeslBundle } from "../WeslBundle.ts";
6
6
  import { expectNoLogAsync } from "./LogCatcher.ts";
7
7
 
8
- test("import rand() from a package", async () => {
9
- const src = `
10
- import random_wgsl::pcg_2u_3f;
8
+ // Static fixture bundle - no external package dependency needed
9
+ const hashPkg: WeslBundle = {
10
+ name: "hash_pkg",
11
+ edition: "unstable_2025_1",
12
+ modules: {
13
+ "hash.wesl":
14
+ "fn hashFn(v: u32) -> u32 { return v ^ (v >> 16u); }\nfn unusedFn() { }",
15
+ },
16
+ };
11
17
 
12
- struct Uniforms { frame: u32 }
13
- @binding(0) @group(0) var<uniform> u: Uniforms;
18
+ test("import fn from a package bundle", async () => {
19
+ const src = `
20
+ import hash_pkg::hash::hashFn;
14
21
 
15
- @fragment
16
- fn fragmentMain(@builtin(position) pos: vec4f) -> @location(0) vec4f {
17
- let rand = pcg_2u_3f(vec2u(pos.xy) + u.frame);
18
- return vec4(rand, 1f);
19
- }
22
+ @compute @workgroup_size(1)
23
+ fn main() { let x = hashFn(1u); }
20
24
  `;
21
-
22
- const weslSrc = { "./main.wesl": src };
23
- const result = await expectNoLogAsync(async () =>
24
- link({ weslSrc, rootModuleName: "./main.wesl", libs: [rand] }),
25
+ const result = await expectNoLogAsync(() =>
26
+ link({
27
+ weslSrc: { "./main.wesl": src },
28
+ rootModuleName: "./main.wesl",
29
+ libs: [hashPkg],
30
+ }),
25
31
  );
26
- expect(result.dest).toContain("fn pcg_2u_3f");
27
- expect(result.dest).not.toContain("sinRand");
32
+ expect(result.dest).toContain("fn hashFn");
33
+ expect(result.dest).not.toContain("unusedFn");
28
34
  });
29
35
 
30
36
  test("import from multi_pkg/second", async () => {