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 +9 -1
- package/dist/index.js +63 -54
- package/package.json +1 -9
- package/src/BindIdents.ts +22 -3
- package/src/discovery/FindUnboundIdents.ts +3 -2
- package/src/test/FindUnboundIdents.test.ts +71 -0
- package/src/test/LinkPackage.test.ts +23 -17
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
|
|
338
|
-
const { refIdentElem, originalName } = ident
|
|
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
|
|
721
|
-
if (elem
|
|
722
|
-
let text = elem
|
|
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
|
|
726
|
-
} else lowerAndEmitElem(elem
|
|
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
|
|
889
|
-
let i = ident
|
|
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
|
|
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
|
|
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
|
|
1139
|
-
if (shortIdents) str.add(identShortString(ident
|
|
1140
|
-
else str.add(identToString(ident
|
|
1141
|
-
if (i < last
|
|
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
|
|
1158
|
-
const { kind, originalName } = ident
|
|
1157
|
+
function identShortString(ident) {
|
|
1158
|
+
const { kind, originalName } = ident;
|
|
1159
1159
|
return `${kind === "decl" ? "%" : ""}${originalName}`;
|
|
1160
1160
|
}
|
|
1161
|
-
function identToString(ident
|
|
1162
|
-
if (!ident
|
|
1163
|
-
const { kind, originalName } = ident
|
|
1164
|
-
const idStr = ident
|
|
1165
|
-
if (kind === "ref") return `${originalName} ${idStr} -> ${identToString(ident
|
|
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
|
|
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
|
|
1659
|
-
if (conditionRef) ident
|
|
1660
|
-
const refIdentElem = makeRefIdentElem(ctx, ident
|
|
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
|
|
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
|
|
2206
|
-
ctx.addElem(body
|
|
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
|
|
3166
|
-
this.state.context.scope.contents.push(ident
|
|
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 (
|
|
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
|
-
|
|
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
|
|
3815
|
-
if (ident
|
|
3816
|
-
if (ident
|
|
3817
|
-
if (ident
|
|
3818
|
-
const foundDecl = findDeclInModule(ident
|
|
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
|
|
3821
|
-
return handleNewDecl(ident
|
|
3828
|
+
ident.refersTo = foundDecl.decl;
|
|
3829
|
+
return handleNewDecl(ident, foundDecl, bindContext);
|
|
3822
3830
|
}
|
|
3823
|
-
if (stdWgsl(ident
|
|
3824
|
-
ident
|
|
3831
|
+
if (stdWgsl(ident.originalName)) {
|
|
3832
|
+
ident.std = true;
|
|
3825
3833
|
return;
|
|
3826
3834
|
}
|
|
3827
|
-
if (!bindContext.unbound) failIdent(ident
|
|
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
|
|
3849
|
-
const found = liveDecls.decls.get(ident
|
|
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
|
|
3860
|
+
moduleAst: ident.ast
|
|
3853
3861
|
};
|
|
3854
|
-
if (liveDecls.parent) return findDeclInModule(ident
|
|
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 =
|
|
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
|
|
4861
|
-
const declElem = findDecl(ident
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
13
|
-
|
|
18
|
+
test("import fn from a package bundle", async () => {
|
|
19
|
+
const src = `
|
|
20
|
+
import hash_pkg::hash::hashFn;
|
|
14
21
|
|
|
15
|
-
@
|
|
16
|
-
fn
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
27
|
-
expect(result.dest).not.toContain("
|
|
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 () => {
|