wesl 0.7.24 → 0.7.26

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
@@ -887,8 +887,16 @@ interface LinkParams {
887
887
  /** function to construct globally unique wgsl identifiers */
888
888
  mangler?: ManglerFn;
889
889
  }
890
- /** Generate a virtual WESL module based on a set of conditions. */
891
- type VirtualLibraryFn = (conditions: Conditions) => string;
890
+ /** Project config for web components and tools. */
891
+ type WeslProject = Pick<LinkParams, "weslSrc" | "rootModuleName" | "conditions" | "constants" | "libs" | "packageName">;
892
+ /** Context passed to virtual library generators. */
893
+ interface VirtualLibContext {
894
+ conditions: Conditions;
895
+ rootModulePath: string;
896
+ packageName: string;
897
+ }
898
+ /** Generate a virtual WESL module. */
899
+ type VirtualLibraryFn = (ctx: VirtualLibContext) => string;
892
900
  /**
893
901
  * Link a set of WESL source modules (typically the text from .wesl files) into a single WGSL string.
894
902
  * Linking starts with a specified 'root' source module, and recursively incorporates code
@@ -925,23 +933,12 @@ declare function normalizeModuleName(name: string): string;
925
933
  //#endregion
926
934
  //#region src/BindIdents.d.ts
927
935
  /**
928
- * BindIdents pass: link reference identifiers to declarations.
929
- *
930
- * Goals:
931
- * - Link reference idents to declaration idents
932
- * - Collect used declarations (to emit in link)
933
- * - Create mangled names for globals to avoid conflicts
936
+ * BindIdents pass: depth-first walk of the scope tree (not syntax tree),
937
+ * linking ref idents to declarations and mangling global names.
934
938
  *
935
- * Algorithm:
936
- * - Recursive depth-first walk of scope tree (not syntax tree)
937
- * - For each ref: search current scope, then up to parent scopes
938
- * - If no local match: check import statements for external matches
939
- * - For found global decls: mangle name to be unique, collect for emission
940
- *
941
- * LiveDecls: tracks visible declarations at the current position, with parent links.
942
- *
943
- * @if/@else: respects conditional compilation by tracking @else validity state,
944
- * mirroring the emission phase's filterValidElements but on scopes.
939
+ * For each ref: search current scope upward, then check imports for external matches.
940
+ * LiveDecls tracks visible declarations with parent links.
941
+ * @if/@else: mirrors filterValidElements but on scopes.
945
942
  */
946
943
  /** Results returned from binding pass. */
947
944
  interface BindResults {
@@ -1012,6 +1009,8 @@ interface BindContext {
1012
1009
  virtuals?: VirtualLibrarySet;
1013
1010
  /** Host package name for resolving package:: in virtual modules. */
1014
1011
  packageName: string;
1012
+ /** Root module path (e.g., "package::main"). */
1013
+ rootModulePath: string;
1015
1014
  /** Unbound identifiers if accumulateUnbound is true. */
1016
1015
  unbound?: UnboundRef[];
1017
1016
  /** Don't follow references from declarations (for library dependency detection). */
@@ -1019,11 +1018,7 @@ interface BindContext {
1019
1018
  /** Visit all conditional branches (for dependency discovery). */
1020
1019
  discoveryMode?: boolean;
1021
1020
  }
1022
- /**
1023
- * Recursively bind references to declarations in this scope and child scopes.
1024
- * Tracks @else state to ensure filtered @else blocks don't pull in unused declarations.
1025
- * @return new declarations found
1026
- */
1021
+ /** Recursively bind refs to decls in this scope and children. @return new declarations found */
1027
1022
  declare function bindIdentsRecursive(scope: Scope, bindContext: BindContext, liveDecls: LiveDecls): DeclIdent[];
1028
1023
  //#endregion
1029
1024
  //#region src/Conditions.d.ts
@@ -1282,4 +1277,4 @@ declare function offsetToLineNumber(offset: number, text: string): [lineNum: num
1282
1277
  */
1283
1278
  declare function errorHighlight(source: string, span: Span): [string, string];
1284
1279
  //#endregion
1285
- export { AbstractElem, AbstractElemBase, AliasElem, Attribute, AttributeElem, BatchModuleResolver, BinaryExpression, BinaryOperator, BindIdentsParams, BindResults, BindingAST, BindingStructElem, BlockStatement, BoundAndTransformed, BuiltinAttribute, BundleResolver, ComponentExpression, ComponentMemberExpression, CompositeResolver, ConditionalAttribute, Conditions, ConstAssertElem, ConstElem, ContainerElem, ContinuingElem, DeclIdent, DeclIdentElem, DeclarationElem, DiagnosticAttribute, DiagnosticDirective, DiagnosticRule, DirectiveElem, DirectiveVariant, ElemKindMap, ElemWithAttributes, ElemWithContentsBase, ElifAttribute, ElseAttribute, EmittableElem, EnableDirective, ExpressionElem, ExtendedGPUValidationError, FnElem, FnParamElem, FunctionCallExpression, GlobalDeclarationElem, GlobalVarElem, GrammarElem, HasAttributes, Ident, IfAttribute, ImportCollection, ImportElem, ImportItem, ImportSegment, ImportStatement, InterpolateAttribute, LetElem, LexicalScope, LinkConfig, LinkParams, LinkRegistryParams, LinkedWesl, LinkerTransform, Literal, LiveDecls, ManglerFn, ModuleElem, ModuleResolver, NameElem, OpenElem, OverrideElem, ParenthesizedExpression, ParseError, type ParseOptions, PartialScope, RecordResolver, RecordResolverOptions, RefIdent, RefIdentElem, RequiresDirective, Scope, ScopeItem, SimpleMemberRef, Span, SrcMap, SrcMapBuilder, SrcMapEntry, SrcModule, SrcPosition, SrcWithPath, StableState, StandardAttribute, StatementElem, StructElem, StructMemberElem, StuffElem, SwitchClauseElem, SyntheticElem, TerminalElem, TextElem, TrackingResolver, TransformedAST, TranslateTimeExpressionElem, TypeRefElem, TypeTemplateParameter, TypedDeclElem, UnaryExpression, UnaryOperator, UnboundRef, UnknownExpressionElem, VarElem, VirtualLibrary, VirtualLibraryFn, VirtualLibrarySet, WeslAST, WeslBundle, WeslDevice, WeslGPUCompilationInfo, WeslGPUCompilationMessage, WeslJsPlugin, WeslParseContext, WeslParseError, WeslParseState, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, childIdent, childScope, containsScope, debug, debugContentsToString, discoverModules, emptyScope, errorHighlight, fileToModulePath, filterMap, filterValidElements, findAllRootDecls, findMap, findRefsToBindingStructs, findUnboundIdents, findUnboundRefs, findValidRootDecls, flatImports, freshResolver, groupBy, grouped, identToString, last, lengthPrefixMangle, link, linkRegistry, liveDeclsToString, log, lowerBindingStructs, makeLiveDecls, makeWeslDevice, mapForward, mapValues, markBindingStructs, markEntryTypes, mergeScope, minimalMangle, minimallyMangledName, modulePartsToRelativePath, moduleToRelativePath, multiKeySet, multisampledTextureTypes, nextIdentId, noSuffix, normalize, normalizeDebugRoot, normalizeModuleName, npmNameVariations, offsetToLineNumber, overlapTail, parseSrcModule, partition, publicDecl, replaceWords, requestWeslDevice, resetScopeIds, resolveModulePath, sampledTextureTypes, sanitizePackageName, scan, scopeToString, scopeToStringLong, srcLog, stdEnumerant, stdEnumerants, stdFn, stdFns, stdType, stdTypes, textureStorageTypes, transformBindingReference, transformBindingStruct, underscoreMangle, validation, wgslStandardAttributes, withLoggerAsync };
1280
+ 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, VirtualLibContext, 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
@@ -3633,9 +3633,8 @@ function flatImports(ast, conditions) {
3633
3633
  //#region src/BindIdents.ts
3634
3634
  /** Bind ref idents to declarations and mangle global declaration names. */
3635
3635
  function bindIdents(params) {
3636
- const { rootAst, resolver, virtuals, accumulateUnbound } = params;
3636
+ const { rootAst, resolver, virtuals, accumulateUnbound, discoveryMode } = params;
3637
3637
  const { conditions = {}, mangler = minimalMangle } = params;
3638
- const { discoveryMode } = params;
3639
3638
  const packageName = rootAst.srcModule.modulePath.split("::")[0];
3640
3639
  const rootDecls = discoveryMode ? findAllRootDecls(rootAst.rootScope) : findValidRootDecls(rootAst.rootScope, conditions);
3641
3640
  const { globalNames, knownDecls } = initRootDecls(rootDecls);
@@ -3646,6 +3645,7 @@ function bindIdents(params) {
3646
3645
  virtuals,
3647
3646
  mangler,
3648
3647
  packageName,
3648
+ rootModulePath: rootAst.srcModule.modulePath,
3649
3649
  foundScopes: /* @__PURE__ */ new Set(),
3650
3650
  globalNames,
3651
3651
  globalStatements: /* @__PURE__ */ new Map(),
@@ -3656,8 +3656,9 @@ function bindIdents(params) {
3656
3656
  decls: new Map(rootDecls.map((d) => [d.originalName, d])),
3657
3657
  parent: null
3658
3658
  };
3659
- const fromRootDecls = rootDecls.flatMap((decl) => processDependentScope(decl, bindContext));
3660
- const fromRefs = bindIdentsRecursive(rootAst.rootScope, bindContext, liveDecls);
3659
+ const fromRootDecls = rootDecls.flatMap((d) => processDependentScope(d, bindContext));
3660
+ const { rootScope } = rootAst;
3661
+ const fromRefs = bindIdentsRecursive(rootScope, bindContext, liveDecls);
3661
3662
  const newStatements = [...bindContext.globalStatements.values()];
3662
3663
  return {
3663
3664
  decls: [...fromRootDecls, ...fromRefs],
@@ -3702,11 +3703,7 @@ function findAllRootDecls(rootScope) {
3702
3703
  function publicDecl(scope, name, conditions) {
3703
3704
  return getValidRootDecls(scope, conditions).find((d) => d.originalName === name);
3704
3705
  }
3705
- /**
3706
- * Recursively bind references to declarations in this scope and child scopes.
3707
- * Tracks @else state to ensure filtered @else blocks don't pull in unused declarations.
3708
- * @return new declarations found
3709
- */
3706
+ /** Recursively bind refs to decls in this scope and children. @return new declarations found */
3710
3707
  function bindIdentsRecursive(scope, bindContext, liveDecls) {
3711
3708
  const { dontFollowDecls, foundScopes } = bindContext;
3712
3709
  if (foundScopes.has(scope)) return [];
@@ -3744,11 +3741,7 @@ function processDependentScope(decl, ctx) {
3744
3741
  if (!rootDecls) return [];
3745
3742
  return bindIdentsRecursive(dependentScope, ctx, makeLiveDecls(rootDecls));
3746
3743
  }
3747
- /**
3748
- * Trace references to their declarations.
3749
- * Mutates to mangle declarations and mark std references.
3750
- * @return found declaration, or undefined if already processed
3751
- */
3744
+ /** Resolve a ref to its declaration, mangling globals and marking std refs. */
3752
3745
  function handleRef(ident, liveDecls, bindContext) {
3753
3746
  if (ident.refersTo || ident.std) return;
3754
3747
  if (ident.conditionRef) return;
@@ -3828,7 +3821,8 @@ function findExport(pathParts, srcModule, ctx) {
3828
3821
  const modulePath = resolveModulePath(pathParts, srcModule.modulePath.split("::")).slice(0, -1).join("::");
3829
3822
  const moduleAst = ctx.resolver.resolveModule(modulePath) ?? virtualModule(pathParts[0], ctx);
3830
3823
  if (!moduleAst) return void 0;
3831
- const decl = publicDecl(moduleAst.rootScope, last(pathParts), ctx.conditions);
3824
+ const name = last(pathParts);
3825
+ const decl = publicDecl(moduleAst.rootScope, name, ctx.conditions);
3832
3826
  if (decl) return {
3833
3827
  decl,
3834
3828
  moduleAst
@@ -3839,9 +3833,14 @@ function virtualModule(moduleName, ctx) {
3839
3833
  const found = ctx.virtuals?.[moduleName];
3840
3834
  if (!found) return void 0;
3841
3835
  if (found.ast) return found.ast;
3842
- const src = found.fn(ctx.conditions);
3836
+ const { conditions, rootModulePath, packageName } = ctx;
3837
+ const src = found.fn({
3838
+ conditions,
3839
+ rootModulePath,
3840
+ packageName
3841
+ });
3843
3842
  found.ast = parseSrcModule({
3844
- modulePath: ctx.packageName + "::" + moduleName,
3843
+ modulePath: packageName + "::" + moduleName,
3845
3844
  debugFilePath: moduleName,
3846
3845
  src
3847
3846
  });
@@ -3861,8 +3860,8 @@ function rootLiveDecls(decl, conditions) {
3861
3860
  assertThatDebug(scope.kind === "scope");
3862
3861
  const root = scope;
3863
3862
  if (!root._scopeDecls) {
3864
- const rootDecls = findValidRootDecls(scope, conditions);
3865
- root._scopeDecls = { decls: new Map(rootDecls.map((d) => [d.originalName, d])) };
3863
+ const decls = findValidRootDecls(scope, conditions);
3864
+ root._scopeDecls = { decls: new Map(decls.map((d) => [d.originalName, d])) };
3866
3865
  }
3867
3866
  return root._scopeDecls;
3868
3867
  }
@@ -4083,6 +4082,7 @@ function findUnboundRefs(resolver) {
4083
4082
  globalStatements: /* @__PURE__ */ new Map(),
4084
4083
  mangler: minimalMangle,
4085
4084
  packageName: "package",
4085
+ rootModulePath: "package::main",
4086
4086
  unbound: [],
4087
4087
  dontFollowDecls: true,
4088
4088
  discoveryMode: true
@@ -4515,8 +4515,9 @@ function flattenLibraryTree(libs) {
4515
4515
  * that share some sources.)
4516
4516
  */
4517
4517
  function linkRegistry(params) {
4518
- const { transformedAst, newDecls, newStatements } = bindAndTransform(params);
4519
- return SrcMapBuilder.build(emitWgsl(transformedAst.moduleElem, transformedAst.srcModule, newDecls, newStatements, params.conditions));
4518
+ const { transformedAst: ast, newDecls, newStatements } = bindAndTransform(params);
4519
+ const builders = emitWgsl(ast.moduleElem, ast.srcModule, newDecls, newStatements, params.conditions);
4520
+ return SrcMapBuilder.build(builders);
4520
4521
  }
4521
4522
  /** Bind identifiers and apply transform plugins */
4522
4523
  function bindAndTransform(params) {
@@ -4574,6 +4575,7 @@ function applyTransformPlugins(rootModule, globalNames, config) {
4574
4575
  };
4575
4576
  return filterMap(config?.plugins ?? [], (plugin) => plugin.transform).reduce((ast, transform) => transform(ast), startAst);
4576
4577
  }
4578
+ /** Assemble WGSL output from prologue statements, root module, and imported declarations. */
4577
4579
  function emitWgsl(rootModuleElem, srcModule, newDecls, newStatements, conditions = {}) {
4578
4580
  const prologueBuilders = newStatements.map((s) => emitStatement(s, conditions));
4579
4581
  const rootBuilder = new SrcMapBuilder({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wesl",
3
- "version": "0.7.24",
3
+ "version": "0.7.26",
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/BindIdents.ts CHANGED
@@ -28,23 +28,12 @@ import {
28
28
  import { last } from "./Util.ts";
29
29
 
30
30
  /**
31
- * BindIdents pass: link reference identifiers to declarations.
31
+ * BindIdents pass: depth-first walk of the scope tree (not syntax tree),
32
+ * linking ref idents to declarations and mangling global names.
32
33
  *
33
- * Goals:
34
- * - Link reference idents to declaration idents
35
- * - Collect used declarations (to emit in link)
36
- * - Create mangled names for globals to avoid conflicts
37
- *
38
- * Algorithm:
39
- * - Recursive depth-first walk of scope tree (not syntax tree)
40
- * - For each ref: search current scope, then up to parent scopes
41
- * - If no local match: check import statements for external matches
42
- * - For found global decls: mangle name to be unique, collect for emission
43
- *
44
- * LiveDecls: tracks visible declarations at the current position, with parent links.
45
- *
46
- * @if/@else: respects conditional compilation by tracking @else validity state,
47
- * mirroring the emission phase's filterValidElements but on scopes.
34
+ * For each ref: search current scope upward, then check imports for external matches.
35
+ * LiveDecls tracks visible declarations with parent links.
36
+ * @if/@else: mirrors filterValidElements but on scopes.
48
37
  */
49
38
 
50
39
  /** Results returned from binding pass. */
@@ -107,9 +96,9 @@ export interface BindIdentsParams
107
96
 
108
97
  /** Bind ref idents to declarations and mangle global declaration names. */
109
98
  export function bindIdents(params: BindIdentsParams): BindResults {
110
- const { rootAst, resolver, virtuals, accumulateUnbound } = params;
99
+ const { rootAst, resolver, virtuals, accumulateUnbound, discoveryMode } =
100
+ params;
111
101
  const { conditions = {}, mangler = minimalMangle } = params;
112
- const { discoveryMode } = params;
113
102
  const packageName = rootAst.srcModule.modulePath.split("::")[0];
114
103
 
115
104
  const rootDecls = discoveryMode
@@ -117,6 +106,7 @@ export function bindIdents(params: BindIdentsParams): BindResults {
117
106
  : findValidRootDecls(rootAst.rootScope, conditions);
118
107
  const { globalNames, knownDecls } = initRootDecls(rootDecls);
119
108
 
109
+ const rootModulePath = rootAst.srcModule.modulePath;
120
110
  const bindContext = {
121
111
  resolver,
122
112
  conditions,
@@ -124,6 +114,7 @@ export function bindIdents(params: BindIdentsParams): BindResults {
124
114
  virtuals,
125
115
  mangler,
126
116
  packageName,
117
+ rootModulePath,
127
118
  foundScopes: new Set<Scope>(),
128
119
  globalNames,
129
120
  globalStatements: new Map<AbstractElem, EmittableElem>(),
@@ -134,15 +125,11 @@ export function bindIdents(params: BindIdentsParams): BindResults {
134
125
  const decls = new Map(rootDecls.map(d => [d.originalName, d] as const));
135
126
  const liveDecls: LiveDecls = { decls, parent: null };
136
127
 
137
- const fromRootDecls = rootDecls.flatMap(decl =>
138
- processDependentScope(decl, bindContext),
139
- );
140
-
141
- const fromRefs = bindIdentsRecursive(
142
- rootAst.rootScope,
143
- bindContext,
144
- liveDecls,
128
+ const fromRootDecls = rootDecls.flatMap(d =>
129
+ processDependentScope(d, bindContext),
145
130
  );
131
+ const { rootScope } = rootAst;
132
+ const fromRefs = bindIdentsRecursive(rootScope, bindContext, liveDecls);
146
133
  const newStatements = [...bindContext.globalStatements.values()];
147
134
  return {
148
135
  decls: [...fromRootDecls, ...fromRefs],
@@ -233,6 +220,9 @@ interface BindContext {
233
220
  /** Host package name for resolving package:: in virtual modules. */
234
221
  packageName: string;
235
222
 
223
+ /** Root module path (e.g., "package::main"). */
224
+ rootModulePath: string;
225
+
236
226
  /** Unbound identifiers if accumulateUnbound is true. */
237
227
  unbound?: UnboundRef[];
238
228
 
@@ -243,11 +233,7 @@ interface BindContext {
243
233
  discoveryMode?: boolean;
244
234
  }
245
235
 
246
- /**
247
- * Recursively bind references to declarations in this scope and child scopes.
248
- * Tracks @else state to ensure filtered @else blocks don't pull in unused declarations.
249
- * @return new declarations found
250
- */
236
+ /** Recursively bind refs to decls in this scope and children. @return new declarations found */
251
237
  export function bindIdentsRecursive(
252
238
  scope: Scope,
253
239
  bindContext: BindContext,
@@ -257,8 +243,11 @@ export function bindIdentsRecursive(
257
243
  if (foundScopes.has(scope)) return [];
258
244
  foundScopes.add(scope);
259
245
 
260
- const result = processScope(scope, bindContext, liveDecls);
261
- const { newGlobals, newFromChildren } = result;
246
+ const { newGlobals, newFromChildren } = processScope(
247
+ scope,
248
+ bindContext,
249
+ liveDecls,
250
+ );
262
251
  const newFromRefs = dontFollowDecls
263
252
  ? []
264
253
  : handleDecls(newGlobals, bindContext);
@@ -301,11 +290,7 @@ function processDependentScope(decl: DeclIdent, ctx: BindContext): DeclIdent[] {
301
290
  return bindIdentsRecursive(dependentScope, ctx, makeLiveDecls(rootDecls));
302
291
  }
303
292
 
304
- /**
305
- * Trace references to their declarations.
306
- * Mutates to mangle declarations and mark std references.
307
- * @return found declaration, or undefined if already processed
308
- */
293
+ /** Resolve a ref to its declaration, mangling globals and marking std refs. */
309
294
  function handleRef(
310
295
  ident: RefIdent,
311
296
  liveDecls: LiveDecls,
@@ -443,11 +428,8 @@ function findExport(
443
428
  ctx.resolver.resolveModule(modulePath) ?? virtualModule(pathParts[0], ctx);
444
429
  if (!moduleAst) return undefined;
445
430
 
446
- const decl = publicDecl(
447
- moduleAst.rootScope,
448
- last(pathParts)!,
449
- ctx.conditions,
450
- );
431
+ const name = last(pathParts)!;
432
+ const decl = publicDecl(moduleAst.rootScope, name, ctx.conditions);
451
433
  if (decl) return { decl, moduleAst };
452
434
  }
453
435
 
@@ -460,10 +442,10 @@ function virtualModule(
460
442
  if (!found) return undefined;
461
443
  if (found.ast) return found.ast;
462
444
 
463
- const src = found.fn(ctx.conditions);
464
- const modulePath = ctx.packageName + "::" + moduleName;
465
- const debugFilePath = moduleName;
466
- found.ast = parseSrcModule({ modulePath, debugFilePath, src });
445
+ const { conditions, rootModulePath, packageName } = ctx;
446
+ const src = found.fn({ conditions, rootModulePath, packageName });
447
+ const modulePath = packageName + "::" + moduleName;
448
+ found.ast = parseSrcModule({ modulePath, debugFilePath: moduleName, src });
467
449
  return found.ast;
468
450
  }
469
451
 
@@ -490,10 +472,8 @@ function rootLiveDecls(
490
472
 
491
473
  const root = scope as LexicalScope;
492
474
  if (!root._scopeDecls) {
493
- const rootDecls = findValidRootDecls(scope, conditions);
494
- root._scopeDecls = {
495
- decls: new Map(rootDecls.map(d => [d.originalName, d])),
496
- };
475
+ const decls = findValidRootDecls(scope, conditions);
476
+ root._scopeDecls = { decls: new Map(decls.map(d => [d.originalName, d])) };
497
477
  }
498
478
  return root._scopeDecls;
499
479
  }
package/src/Linker.ts CHANGED
@@ -91,8 +91,26 @@ export interface LinkParams {
91
91
  mangler?: ManglerFn;
92
92
  }
93
93
 
94
- /** Generate a virtual WESL module based on a set of conditions. */
95
- export type VirtualLibraryFn = (conditions: Conditions) => string;
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
+
105
+ /** Context passed to virtual library generators. */
106
+ export interface VirtualLibContext {
107
+ conditions: Conditions;
108
+ rootModulePath: string;
109
+ packageName: string;
110
+ }
111
+
112
+ /** Generate a virtual WESL module. */
113
+ export type VirtualLibraryFn = (ctx: VirtualLibContext) => string;
96
114
 
97
115
  /**
98
116
  * Link a set of WESL source modules (typically the text from .wesl files) into a single WGSL string.
@@ -182,18 +200,19 @@ export interface LinkRegistryParams
182
200
  * that share some sources.)
183
201
  */
184
202
  export function linkRegistry(params: LinkRegistryParams): SrcMap {
185
- const bound = bindAndTransform(params);
186
- const { transformedAst, newDecls, newStatements } = bound;
187
-
188
- return SrcMapBuilder.build(
189
- emitWgsl(
190
- transformedAst.moduleElem,
191
- transformedAst.srcModule,
192
- newDecls,
193
- newStatements,
194
- params.conditions,
195
- ),
203
+ const {
204
+ transformedAst: ast,
205
+ newDecls,
206
+ newStatements,
207
+ } = bindAndTransform(params);
208
+ const builders = emitWgsl(
209
+ ast.moduleElem,
210
+ ast.srcModule,
211
+ newDecls,
212
+ newStatements,
213
+ params.conditions,
196
214
  );
215
+ return SrcMapBuilder.build(builders);
197
216
  }
198
217
 
199
218
  export interface BoundAndTransformed {
@@ -214,15 +233,14 @@ export function bindAndTransform(
214
233
 
215
234
  const virtuals = setupVirtualLibs(params.virtualLibs, constants);
216
235
 
217
- const bindParams = {
236
+ const bound = bindIdents({
218
237
  rootAst,
219
238
  resolver,
220
239
  conditions,
221
240
  virtuals,
222
241
  mangler,
223
- };
224
- const bindResults = bindIdents(bindParams);
225
- const { globalNames, decls: newDecls, newStatements } = bindResults;
242
+ });
243
+ const { globalNames, decls: newDecls, newStatements } = bound;
226
244
 
227
245
  const transformedAst = applyTransformPlugins(rootAst, globalNames, config);
228
246
  return { transformedAst, newDecls, newStatements };
@@ -264,7 +282,7 @@ function setupVirtualLibs(
264
282
  ): VirtualLibrarySet | undefined {
265
283
  let libs = virtualLibs;
266
284
  if (constants) {
267
- const constantsGen = () =>
285
+ const constantsGen: VirtualLibraryFn = () =>
268
286
  Object.entries(constants)
269
287
  .map(([name, value]) => `const ${name} = ${value};`)
270
288
  .join("\n");
@@ -284,14 +302,10 @@ function applyTransformPlugins(
284
302
  const startAst = { moduleElem, srcModule, globalNames, notableElems: {} };
285
303
  const plugins = config?.plugins ?? [];
286
304
  const transforms = filterMap(plugins, plugin => plugin.transform);
287
- const transformedAst = transforms.reduce(
288
- (ast, transform) => transform(ast),
289
- startAst,
290
- );
291
-
292
- return transformedAst;
305
+ return transforms.reduce((ast, transform) => transform(ast), startAst);
293
306
  }
294
307
 
308
+ /** Assemble WGSL output from prologue statements, root module, and imported declarations. */
295
309
  function emitWgsl(
296
310
  rootModuleElem: ModuleElem,
297
311
  srcModule: SrcModule,
@@ -342,7 +356,6 @@ function emitDecl(decl: DeclIdent, conditions: Conditions): SrcMapBuilder {
342
356
  return builder;
343
357
  }
344
358
 
345
- /* ---- Commentary on present and future features ---- */
346
359
  /*
347
360
 
348
361
  LATER
@@ -42,6 +42,7 @@ export function findUnboundRefs(resolver: BatchModuleResolver): UnboundRef[] {
42
42
  globalStatements: new Map<AbstractElem, EmittableElem>(),
43
43
  mangler: minimalMangle,
44
44
  packageName: "package",
45
+ rootModulePath: "package::main",
45
46
  unbound: [] as UnboundRef[],
46
47
  dontFollowDecls: true,
47
48
  discoveryMode: true,