wgsl-edit 0.0.18 → 0.0.20

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.
@@ -1,4 +1,4 @@
1
- import { Conditions, LinkParams } from "wesl";
1
+ import { Conditions, LinkParams, WeslBundle } from "wesl";
2
2
 
3
3
  //#region src/WgslEdit.d.ts
4
4
  type WeslProject = Pick<LinkParams, "weslSrc" | "rootModuleName" | "conditions" | "constants" | "libs" | "packageName">;
@@ -20,6 +20,7 @@ declare class WgslEdit extends HTMLElement {
20
20
  private _lineNumbers;
21
21
  private _files;
22
22
  private _activeFile;
23
+ private _rootModuleName;
23
24
  private _tabs;
24
25
  private _lint;
25
26
  private _fetchLibs;
@@ -54,7 +55,12 @@ declare class WgslEdit extends HTMLElement {
54
55
  set project(value: WeslProject);
55
56
  /** Link/compile WESL sources into WGSL. Returns the compiled WGSL string. */
56
57
  link(options?: Partial<LinkParams>): Promise<string>;
57
- /** Currently active file name. */
58
+ /** Library bundles for linking (set via project). */
59
+ get libs(): WeslBundle[];
60
+ /** Root module for linking (stable across tab switches). */
61
+ get rootModuleName(): string | undefined;
62
+ set rootModuleName(value: string | undefined);
63
+ /** Currently active file name (selected tab). */
58
64
  get activeFile(): string;
59
65
  /** Switch to a file by name. */
60
66
  set activeFile(name: string);
package/dist/WgslEdit.js CHANGED
@@ -61,13 +61,14 @@ var WgslEdit = class extends HTMLElement {
61
61
  _lineNumbers = false;
62
62
  _files = /* @__PURE__ */ new Map();
63
63
  _activeFile = "";
64
+ _rootModuleName;
64
65
  _tabs = true;
65
66
  _lint = "on";
66
67
  _fetchLibs = true;
67
68
  _conditions = {};
68
69
  _packageName;
69
70
  _libs = [];
70
- _ignorePackages = ["constants", "test"];
71
+ _ignorePackages = ["constants", "env"];
71
72
  _fetchingPkgs = /* @__PURE__ */ new Set();
72
73
  _fetchedPkgs = /* @__PURE__ */ new Set();
73
74
  _snackTimer;
@@ -181,16 +182,18 @@ var WgslEdit = class extends HTMLElement {
181
182
  if (conditions !== void 0) this._conditions = conditions;
182
183
  if (packageName !== void 0) this._packageName = packageName;
183
184
  if (libs !== void 0) this._libs = libs;
185
+ if (rootModuleName !== void 0) this._rootModuleName = rootModuleName;
184
186
  if (weslSrc) {
185
187
  this.sources = weslSrc;
186
- if (rootModuleName) this.activeFile = toTabName(rootModuleName);
188
+ const tab = toTabName(rootModuleName ?? this._rootModuleName ?? "");
189
+ if (tab) this.activeFile = tab;
187
190
  }
188
191
  this.updateLint();
189
192
  }
190
193
  /** Link/compile WESL sources into WGSL. Returns the compiled WGSL string. */
191
194
  async link(options) {
192
195
  const pkg = this._packageName ?? "package";
193
- const rootModuleName = fileToModulePath(this._activeFile, pkg, false);
196
+ const rootModuleName = this._rootModuleName ?? fileToModulePath(this._activeFile, pkg, false);
194
197
  return (await link({
195
198
  weslSrc: this.sources,
196
199
  rootModuleName,
@@ -200,7 +203,19 @@ var WgslEdit = class extends HTMLElement {
200
203
  ...options
201
204
  })).dest;
202
205
  }
203
- /** Currently active file name. */
206
+ /** Library bundles for linking (set via project). */
207
+ get libs() {
208
+ return this._libs;
209
+ }
210
+ /** Root module for linking (stable across tab switches). */
211
+ get rootModuleName() {
212
+ return this._rootModuleName;
213
+ }
214
+ set rootModuleName(value) {
215
+ this._rootModuleName = value;
216
+ this.dispatchChange();
217
+ }
218
+ /** Currently active file name (selected tab). */
204
219
  get activeFile() {
205
220
  return this._activeFile;
206
221
  }
@@ -348,11 +363,11 @@ var WgslEdit = class extends HTMLElement {
348
363
  }
349
364
  }
350
365
  dispatchChange() {
351
- const { source, sources, conditions, _activeFile: activeFile } = this;
366
+ const { source, sources, conditions, _rootModuleName: rootModuleName } = this;
352
367
  const detail = {
353
368
  source,
354
369
  sources,
355
- activeFile,
370
+ rootModuleName,
356
371
  conditions
357
372
  };
358
373
  this.dispatchEvent(new CustomEvent("change", { detail }));
package/dist/wgsl-edit.js CHANGED
@@ -19307,11 +19307,78 @@ function emptyScope(parent, kind = "scope") {
19307
19307
  }
19308
19308
 
19309
19309
  //#endregion
19310
- //#region ../wesl/src/LowerAndEmit.ts
19311
- /** Valid WGSL standard attributes (from spec). Non-WGSL attributes are stripped.
19312
- * See: https://www.w3.org/TR/WGSL/#attributes
19313
- * Note: @builtin, @diagnostic, @interpolate are parsed as separate attribute types. */
19314
- const wgslStandardAttributes$1 = new Set([
19310
+ //#region ../wesl/src/StandardTypes.ts
19311
+ const stdFns = `bitcast all any select arrayLength
19312
+ abs acos acosh asin asinh atan atanh atan2 ceil clamp cos cosh
19313
+ countLeadingZeros countOneBits countTrailingZeros cross
19314
+ degrees determinant distance dot dot4U8Packed dot4I8Packed
19315
+ exp exp2 extractBits faceForward firstLeadingBit firstTrailingBit
19316
+ floor fma fract frexp insertBits inverseSqrt ldexp length log log2
19317
+ max min mix modf normalize pow quantizeToF16 radians reflect refract
19318
+ reverseBits round saturate sign sin sinh smoothstep sqrt step tan tanh
19319
+ transpose trunc
19320
+ dpdx dpdxCoarse dpdxFine dpdy dpdyCoarse dpdyFine fwidth
19321
+ fwidthCoarse fwidthFine
19322
+ textureDimensions textureGather textureGatherCompare textureLoad
19323
+ textureNumLayers textureNumLevels textureNumSamples
19324
+ textureSample textureSampleBias textureSampleCompare textureSampleCompareLevel
19325
+ textureSampleGrad textureSampleLevel textureSampleBaseClampToEdge
19326
+ textureStore
19327
+ atomicLoad atomicStore atomicAdd atomicSub atomicMax atomicMin
19328
+ atomicAnd atomicOr atomicXor atomicExchange atomicCompareExchangeWeak
19329
+ pack4x8snorm pack4x8unorm pack4xI8 pack4xU8 pack4xI8Clamp pack4xU8Clamp
19330
+ pack2x16snorm pack2x16unorm pack2x16float
19331
+ unpack4x8snorm unpack4x8unorm unpack4xI8 unpack4xU8
19332
+ unpack2x16snorm unpack2x16unorm unpack2x16float
19333
+ storageBarrier textureBarrier workgroupBarrier workgroupUniformLoad
19334
+ subgroupAdd subgroupAll subgroupAnd subgroupAny subgroupBallot
19335
+ subgroupBroadcast subgroupBroadcastFirst subgroupElect
19336
+ subgroupExclusiveAdd subgroupExclusiveMul subgroupInclusiveAdd
19337
+ subgroupInclusiveMul subgroupMax subgroupMin subgroupMul subgroupOr
19338
+ subgroupShuffle subgroupShuffleUp subgroupShuffleXor subgroupXor
19339
+ quadBroadcast quadSwapDiagonal quadSwapX quadSwapY`.split(/\s+/);
19340
+ const sampledTextureTypes = `
19341
+ texture_1d texture_2d texture_2d_array texture_3d
19342
+ texture_cube texture_cube_array
19343
+ `;
19344
+ const multisampledTextureTypes = `
19345
+ texture_multisampled_2d texture_depth_multisampled_2d
19346
+ `;
19347
+ const textureStorageTypes = `
19348
+ texture_storage_1d texture_storage_2d texture_storage_2d_array
19349
+ texture_storage_3d
19350
+ `;
19351
+ const stdTypes = `array atomic bool f16 f32 i32
19352
+ mat2x2 mat2x3 mat2x4 mat3x2 mat3x3 mat3x4 mat4x2 mat4x3 mat4x4
19353
+ mat2x2f mat2x3f mat2x4f mat3x2f mat3x3f mat3x4f
19354
+ mat4x2f mat4x3f mat4x4f
19355
+ mat2x2h mat2x3h mat2x4h mat3x2h mat3x3h mat3x4h
19356
+ mat4x2h mat4x3h mat4x4h
19357
+ u32 vec2 vec3 vec4 ptr
19358
+ vec2i vec3i vec4i vec2u vec3u vec4u
19359
+ vec2f vec3f vec4f vec2h vec3h vec4h
19360
+ ${sampledTextureTypes}
19361
+ ${multisampledTextureTypes}
19362
+ texture_external
19363
+ ${textureStorageTypes}
19364
+ texture_depth_2d texture_depth_2d_array texture_depth_cube
19365
+ texture_depth_cube_array
19366
+ sampler sampler_comparison
19367
+ rgba8unorm rgba8snorm rgba8uint rgba8sint
19368
+ rgba16uint rgba16sint rgba16float
19369
+ r32uint r32sint r32float rg32uint rg32sint rg32float
19370
+ rgba32uint rgba32sint rgba32float
19371
+ bgra8unorm`.split(/\s+/);
19372
+ /** https://www.w3.org/TR/WGSL/#predeclared-enumerants */
19373
+ const stdEnumerants = `read write read_write
19374
+ function private workgroup uniform storage
19375
+ rgba8unorm rgba8snorm rgba8uint rgba8sint
19376
+ rgba16uint rgba16sint rgba16float
19377
+ r32uint r32sint r32float rg32uint rg32sint rg32float
19378
+ rgba32uint rgba32sint rgba32float bgra8unorm`.split(/\s+/);
19379
+ /** WGSL standard attributes whose params need binding (e.g., @workgroup_size).
19380
+ * See: https://www.w3.org/TR/WGSL/#attributes */
19381
+ const wgslStandardAttributes = new Set([
19315
19382
  "align",
19316
19383
  "binding",
19317
19384
  "blend_src",
@@ -19327,6 +19394,21 @@ const wgslStandardAttributes$1 = new Set([
19327
19394
  "vertex",
19328
19395
  "workgroup_size"
19329
19396
  ]);
19397
+ /** return true if the name is for a built in type (not a user struct) */
19398
+ function stdType(name) {
19399
+ return stdTypes.includes(name);
19400
+ }
19401
+ /** return true if the name is for a built in fn (not a user function) */
19402
+ function stdFn(name) {
19403
+ return stdFns.includes(name) || stdType(name);
19404
+ }
19405
+ /** return true if the name is for a built in enumerant */
19406
+ function stdEnumerant(name) {
19407
+ return stdEnumerants.includes(name);
19408
+ }
19409
+
19410
+ //#endregion
19411
+ //#region ../wesl/src/LowerAndEmit.ts
19330
19412
  /** Traverse the AST, starting from root elements, emitting WGSL for each. */
19331
19413
  function lowerAndEmit(params) {
19332
19414
  const { srcBuilder, rootElems, conditions } = params;
@@ -19606,7 +19688,7 @@ function emitAttribute(e, ctx) {
19606
19688
  const { kind } = e.attribute;
19607
19689
  if (kind === "@if" || kind === "@elif" || kind === "@else") return false;
19608
19690
  if (kind === "@attribute") {
19609
- if (!wgslStandardAttributes$1.has(e.attribute.name)) return false;
19691
+ if (!wgslStandardAttributes.has(e.attribute.name)) return false;
19610
19692
  emitStandardAttribute(e, ctx);
19611
19693
  return true;
19612
19694
  }
@@ -22087,115 +22169,16 @@ function flatImports(ast, conditions) {
22087
22169
  return flat;
22088
22170
  }
22089
22171
 
22090
- //#endregion
22091
- //#region ../wesl/src/StandardTypes.ts
22092
- const stdFns = `bitcast all any select arrayLength
22093
- abs acos acosh asin asinh atan atanh atan2 ceil clamp cos cosh
22094
- countLeadingZeros countOneBits countTrailingZeros cross
22095
- degrees determinant distance dot dot4U8Packed dot4I8Packed
22096
- exp exp2 extractBits faceForward firstLeadingBit firstTrailingBit
22097
- floor fma fract frexp insertBits inverseSqrt ldexp length log log2
22098
- max min mix modf normalize pow quantizeToF16 radians reflect refract
22099
- reverseBits round saturate sign sin sinh smoothstep sqrt step tan tanh
22100
- transpose trunc
22101
- dpdx dpdxCoarse dpdxFine dpdy dpdyCoarse dpdyFine fwidth
22102
- fwidthCoarse fwidthFine
22103
- textureDimensions textureGather textureGatherCompare textureLoad
22104
- textureNumLayers textureNumLevels textureNumSamples
22105
- textureSample textureSampleBias textureSampleCompare textureSampleCompareLevel
22106
- textureSampleGrad textureSampleLevel textureSampleBaseClampToEdge
22107
- textureStore
22108
- atomicLoad atomicStore atomicAdd atomicSub atomicMax atomicMin
22109
- atomicAnd atomicOr atomicXor atomicExchange atomicCompareExchangeWeak
22110
- pack4x8snorm pack4x8unorm pack4xI8 pack4xU8 pack4xI8Clamp pack4xU8Clamp
22111
- pack2x16snorm pack2x16unorm pack2x16float
22112
- unpack4x8snorm unpack4x8unorm unpack4xI8 unpack4xU8
22113
- unpack2x16snorm unpack2x16unorm unpack2x16float
22114
- storageBarrier textureBarrier workgroupBarrier workgroupUniformLoad
22115
- subgroupAdd subgroupAll subgroupAnd subgroupAny subgroupBallot
22116
- subgroupBroadcast subgroupBroadcastFirst subgroupElect
22117
- subgroupExclusiveAdd subgroupExclusiveMul subgroupInclusiveAdd
22118
- subgroupInclusiveMul subgroupMax subgroupMin subgroupMul subgroupOr
22119
- subgroupShuffle subgroupShuffleUp subgroupShuffleXor subgroupXor
22120
- quadBroadcast quadSwapDiagonal quadSwapX quadSwapY`.split(/\s+/);
22121
- const sampledTextureTypes = `
22122
- texture_1d texture_2d texture_2d_array texture_3d
22123
- texture_cube texture_cube_array
22124
- `;
22125
- const multisampledTextureTypes = `
22126
- texture_multisampled_2d texture_depth_multisampled_2d
22127
- `;
22128
- const textureStorageTypes = `
22129
- texture_storage_1d texture_storage_2d texture_storage_2d_array
22130
- texture_storage_3d
22131
- `;
22132
- const stdTypes = `array atomic bool f16 f32 i32
22133
- mat2x2 mat2x3 mat2x4 mat3x2 mat3x3 mat3x4 mat4x2 mat4x3 mat4x4
22134
- mat2x2f mat2x3f mat2x4f mat3x2f mat3x3f mat3x4f
22135
- mat4x2f mat4x3f mat4x4f
22136
- mat2x2h mat2x3h mat2x4h mat3x2h mat3x3h mat3x4h
22137
- mat4x2h mat4x3h mat4x4h
22138
- u32 vec2 vec3 vec4 ptr
22139
- vec2i vec3i vec4i vec2u vec3u vec4u
22140
- vec2f vec3f vec4f vec2h vec3h vec4h
22141
- ${sampledTextureTypes}
22142
- ${multisampledTextureTypes}
22143
- texture_external
22144
- ${textureStorageTypes}
22145
- texture_depth_2d texture_depth_2d_array texture_depth_cube
22146
- texture_depth_cube_array
22147
- sampler sampler_comparison
22148
- rgba8unorm rgba8snorm rgba8uint rgba8sint
22149
- rgba16uint rgba16sint rgba16float
22150
- r32uint r32sint r32float rg32uint rg32sint rg32float
22151
- rgba32uint rgba32sint rgba32float
22152
- bgra8unorm`.split(/\s+/);
22153
- /** https://www.w3.org/TR/WGSL/#predeclared-enumerants */
22154
- const stdEnumerants = `read write read_write
22155
- function private workgroup uniform storage
22156
- rgba8unorm rgba8snorm rgba8uint rgba8sint
22157
- rgba16uint rgba16sint rgba16float
22158
- r32uint r32sint r32float rg32uint rg32sint rg32float
22159
- rgba32uint rgba32sint rgba32float bgra8unorm`.split(/\s+/);
22160
- /** return true if the name is for a built in type (not a user struct) */
22161
- function stdType(name) {
22162
- return stdTypes.includes(name);
22163
- }
22164
- /** return true if the name is for a built in fn (not a user function) */
22165
- function stdFn(name) {
22166
- return stdFns.includes(name) || stdType(name);
22167
- }
22168
- /** return true if the name is for a built in enumerant */
22169
- function stdEnumerant(name) {
22170
- return stdEnumerants.includes(name);
22171
- }
22172
-
22173
22172
  //#endregion
22174
22173
  //#region ../wesl/src/BindIdents.ts
22175
- /** WGSL standard attributes whose params need binding (e.g., @workgroup_size). */
22176
- const wgslStandardAttributes = new Set([
22177
- "align",
22178
- "binding",
22179
- "blend_src",
22180
- "compute",
22181
- "const",
22182
- "fragment",
22183
- "group",
22184
- "id",
22185
- "invariant",
22186
- "location",
22187
- "must_use",
22188
- "size",
22189
- "vertex",
22190
- "workgroup_size"
22191
- ]);
22192
22174
  /** Bind ref idents to declarations and mangle global declaration names. */
22193
22175
  function bindIdents(params) {
22194
22176
  const { rootAst, resolver, virtuals, accumulateUnbound } = params;
22195
22177
  const { conditions = {}, mangler = minimalMangle } = params;
22178
+ const { discoveryMode } = params;
22196
22179
  const packageName = rootAst.srcModule.modulePath.split("::")[0];
22197
- const validRootDecls = findValidRootDecls(rootAst.rootScope, conditions);
22198
- const { globalNames, knownDecls } = initRootDecls(validRootDecls);
22180
+ const rootDecls = discoveryMode ? findAllRootDecls(rootAst.rootScope) : findValidRootDecls(rootAst.rootScope, conditions);
22181
+ const { globalNames, knownDecls } = initRootDecls(rootDecls);
22199
22182
  const bindContext = {
22200
22183
  resolver,
22201
22184
  conditions,
@@ -22206,13 +22189,14 @@ function bindIdents(params) {
22206
22189
  foundScopes: /* @__PURE__ */ new Set(),
22207
22190
  globalNames,
22208
22191
  globalStatements: /* @__PURE__ */ new Map(),
22209
- unbound: accumulateUnbound ? [] : void 0
22192
+ unbound: accumulateUnbound ? [] : void 0,
22193
+ discoveryMode
22210
22194
  };
22211
22195
  const liveDecls = {
22212
- decls: new Map(validRootDecls.map((d) => [d.originalName, d])),
22196
+ decls: new Map(rootDecls.map((d) => [d.originalName, d])),
22213
22197
  parent: null
22214
22198
  };
22215
- const fromRootDecls = validRootDecls.flatMap((decl) => processDependentScope(decl, bindContext));
22199
+ const fromRootDecls = rootDecls.flatMap((decl) => processDependentScope(decl, bindContext));
22216
22200
  const fromRefs = bindIdentsRecursive(rootAst.rootScope, bindContext, liveDecls);
22217
22201
  const newStatements = [...bindContext.globalStatements.values()];
22218
22202
  return {
@@ -22248,10 +22232,11 @@ function* validItems(scope, conditions) {
22248
22232
  }
22249
22233
  /** Find all conditionally valid declarations at the root level. */
22250
22234
  function findValidRootDecls(rootScope, conditions) {
22251
- const found = [];
22252
- for (const item of validItems(rootScope, conditions)) if (item.kind === "decl") found.push(item);
22253
- else if (item.kind === "partial") collectDecls(item, found);
22254
- return found;
22235
+ return collectDecls(validItems(rootScope, conditions));
22236
+ }
22237
+ /** Find all declarations at the root level, ignoring conditions. */
22238
+ function findAllRootDecls(rootScope) {
22239
+ return collectDecls(rootScope.contents);
22255
22240
  }
22256
22241
  /** Find a public declaration with the given original name. */
22257
22242
  function publicDecl(scope, name, conditions) {
@@ -22319,10 +22304,11 @@ function handleRef(ident, liveDecls, bindContext) {
22319
22304
  }
22320
22305
  if (!bindContext.unbound) failIdent(ident, `unresolved identifier '${ident.originalName}'`);
22321
22306
  }
22307
+ /** Follow new global declarations into their dependent scopes. */
22322
22308
  function handleDecls(newGlobals, bindContext) {
22323
22309
  return newGlobals.flatMap((decl) => processDependentScope(decl, bindContext));
22324
22310
  }
22325
- /** If found declaration is new, mangle its name. Return if it's a global declaration. */
22311
+ /** If found declaration is new, mangle its name. @return the decl if it's global. */
22326
22312
  function handleNewDecl(refIdent, foundDecl, ctx) {
22327
22313
  const { decl, moduleAst } = foundDecl;
22328
22314
  const { knownDecls, globalNames, mangler, globalStatements } = ctx;
@@ -22349,7 +22335,8 @@ function findDeclInModule(ident, liveDecls) {
22349
22335
  /** Match a ref ident to a declaration in another module via import or qualified ident. */
22350
22336
  function findQualifiedImport(refIdent, ctx) {
22351
22337
  const { conditions, unbound, discoveryMode } = ctx;
22352
- const flatImps = flatImports(refIdent.ast, discoveryMode ? void 0 : conditions);
22338
+ const conds = discoveryMode ? void 0 : conditions;
22339
+ const flatImps = flatImports(refIdent.ast, conds);
22353
22340
  const identParts = refIdent.originalName.split("::");
22354
22341
  const pathParts = matchingImport(identParts, flatImps) ?? qualifiedIdent(identParts);
22355
22342
  if (!pathParts) {
@@ -22403,7 +22390,7 @@ function virtualModule(moduleName, ctx) {
22403
22390
  /** Get cached valid root declarations, computing on first access. */
22404
22391
  function getValidRootDecls(rootScope, conditions) {
22405
22392
  const lexScope = rootScope;
22406
- if (!lexScope._validRootDecls) lexScope._validRootDecls = findValidRootDecls(rootScope, conditions);
22393
+ lexScope._validRootDecls ??= findValidRootDecls(rootScope, conditions);
22407
22394
  return lexScope._validRootDecls;
22408
22395
  }
22409
22396
  /** Given a global declIdent, return the liveDecls for its root scope. */
@@ -22432,150 +22419,18 @@ function setMangledName(proposedName, decl, globalNames, srcModule, mangler) {
22432
22419
  function stdWgsl(name) {
22433
22420
  return stdType(name) || stdFn(name) || stdEnumerant(name);
22434
22421
  }
22422
+ /** @return identParts if it's a qualified path (has ::). */
22435
22423
  function qualifiedIdent(identParts) {
22436
22424
  if (identParts.length > 1) return identParts;
22437
22425
  }
22438
- /** Collect all declarations in a scope (used when scope is already validated). */
22439
- function collectDecls(scope, found) {
22440
- for (const item of scope.contents) if (item.kind === "decl") found.push(item);
22441
- else if (item.kind === "partial") collectDecls(item, found);
22442
- }
22443
-
22444
- //#endregion
22445
- //#region ../wesl/src/discovery/PackageNameUtils.ts
22446
- /** Generate npm package name variations from sanitized WESL identifier.
22447
- *
22448
- * Uses double-underscore encoding to distinguish scoped vs unscoped packages:
22449
- * - Has __ → scoped package (try @scope/pkg variants)
22450
- * - No __ → unscoped package (try pkg variants)
22451
- *
22452
- * Examples:
22453
- * "lygia__shader_utils" → ["@lygia/shader_utils", "@lygia/shader-utils"]
22454
- * "random_wgsl" → ["random_wgsl", "random-wgsl"]
22455
- */
22456
- function* npmNameVariations(sanitizedPath) {
22457
- const [pkg, sub] = breakAt(sanitizedPath, "/");
22458
- let pkgName = pkg;
22459
- let scopePrefix = "";
22460
- if (pkg.includes("__")) {
22461
- const [scope, ...rest] = pkg.split("__");
22462
- pkgName = rest.join("__");
22463
- scopePrefix = `@${scope}/`;
22464
- }
22465
- yield `${scopePrefix}${pkgName}${sub}`;
22466
- yield `${scopePrefix}${pkgName.replaceAll("_", "-")}${sub}`;
22467
- }
22468
- /** Break string at first occurrence of delimiter.
22469
- * @returns [before, after] where after includes the delimiter */
22470
- function breakAt(str, delimiter) {
22471
- const index = str.indexOf(delimiter);
22472
- if (index === -1) return [str, ""];
22473
- return [str.slice(0, index), str.slice(index)];
22474
- }
22475
-
22476
- //#endregion
22477
- //#region ../wesl/src/LinkedWesl.ts
22478
- /**
22479
- * Multiple WESL files that have been linked together to produce WGSL code.
22480
- *
22481
- * Call {@link LinkedWesl.createShaderModule} on a {@link WeslDevice}
22482
- * to make the error reporting aware of the WESL code.
22483
- */
22484
- var LinkedWesl = class {
22485
- sourceMap;
22486
- constructor(sourceMap) {
22487
- this.sourceMap = sourceMap;
22488
- }
22489
- /**
22490
- * Creates a {@link GPUShaderModule}.
22491
- * When errors occur, they will point at the original WESL source code.
22492
- *
22493
- * The compilation info {@link GPUShaderModule.getCompilationInfo}
22494
- * can be remapped with {@link mapGPUCompilationInfo}
22495
- * @param device GPUDevice. Preferably a {@link WeslDevice} for better error reporting.
22496
- * @param descriptor - Description of the {@link GPUShaderModule} to create.
22497
- */
22498
- createShaderModule(device, descriptor) {
22499
- if (!("injectError" in device)) return device.createShaderModule({
22500
- ...descriptor,
22501
- code: this.dest
22502
- });
22503
- device.pushErrorScope("validation");
22504
- const module = device.createShaderModule({
22505
- ...descriptor,
22506
- code: this.dest
22507
- });
22508
- device.popErrorScope();
22509
- const { promise, resolve } = Promise.withResolvers();
22510
- device.injectError("validation", promise);
22511
- module.getCompilationInfo().then((compilationInfo) => {
22512
- if (compilationInfo.messages.length === 0) {
22513
- resolve(null);
22514
- return;
22515
- }
22516
- const mappedCompilationInfo = this.mapGPUCompilationInfo(compilationInfo);
22517
- const errorMessage = compilationInfoToErrorMessage(mappedCompilationInfo, module);
22518
- assertThatDebug(errorMessage !== null);
22519
- const error = new GPUValidationError(errorMessage);
22520
- error.cause = /* @__PURE__ */ new Error("createShaderModule failed");
22521
- error.compilationInfo = mappedCompilationInfo;
22522
- resolve(error);
22523
- });
22524
- return module;
22525
- }
22526
- /**
22527
- * Use {@link LinkedWesl.createShaderModule} for a
22528
- * better error reporting experience.
22529
- */
22530
- get dest() {
22531
- return this.sourceMap.dest.text;
22532
- }
22533
- /** Turns raw compilation info into compilation info
22534
- * that points at the WESL sources. */
22535
- mapGPUCompilationInfo(compilationInfo) {
22536
- return {
22537
- __brand: compilationInfo.__brand,
22538
- messages: compilationInfo.messages.map((v) => this.mapGPUCompilationMessage(v))
22539
- };
22540
- }
22541
- mapGPUCompilationMessage(message) {
22542
- const srcMap = this.sourceMap;
22543
- const srcPosition = srcMap.destToSrc(message.offset);
22544
- const length = (message.length > 0 ? srcMap.destToSrc(message.offset + message.length) : srcPosition).position - srcPosition.position;
22545
- const [lineNum, linePos] = offsetToLineNumber(srcPosition.position, srcPosition.src.text);
22546
- return {
22547
- __brand: message.__brand,
22548
- type: message.type,
22549
- message: message.message,
22550
- offset: srcPosition.position,
22551
- length,
22552
- lineNum,
22553
- linePos,
22554
- module: {
22555
- url: srcPosition.src.path ?? "",
22556
- text: srcPosition.src.text
22557
- }
22558
- };
22559
- }
22560
- };
22561
- /**
22562
- * Tries to imitate the way the browser logs the compilation info.
22563
- * Does not do the remapping.
22564
- * @returns A string with errors, or `null` if there were no compilation messages.
22565
- */
22566
- function compilationInfoToErrorMessage(compilationInfo, shaderModule) {
22567
- if (compilationInfo.messages.length === 0) return null;
22568
- let result = `Compilation log for [Invalid ShaderModule (${shaderModule.label || "unlabled"})]:\n`;
22569
- const errorCount = compilationInfo.messages.filter((v) => v.type === "error").length;
22570
- if (errorCount > 0) result += `${errorCount} error(s) generated while compiling the shader:\n`;
22571
- for (const message of compilationInfo.messages) {
22572
- const { lineNum, linePos } = message;
22573
- result += `${message.module.url}:${lineNum}:${linePos}`;
22574
- result += ` ${message.type}: ${message.message}\n`;
22575
- const source = message.module.text;
22576
- if (source) result += errorHighlight(source, [message.offset, message.offset + message.length]).join("\n");
22577
- }
22578
- return result;
22426
+ /** Collect all declarations from scope items, recursing into partial scopes. */
22427
+ function collectDecls(items) {
22428
+ return [...items].flatMap((item) => {
22429
+ const { kind } = item;
22430
+ if (kind === "decl") return [item];
22431
+ if (kind === "partial") return collectDecls(item.contents);
22432
+ return [];
22433
+ });
22579
22434
  }
22580
22435
 
22581
22436
  //#endregion
@@ -22727,6 +22582,143 @@ function findInVariants(sources, basePath, extensions = ["wesl", "wgsl"]) {
22727
22582
  }
22728
22583
  }
22729
22584
 
22585
+ //#endregion
22586
+ //#region ../wesl/src/discovery/PackageNameUtils.ts
22587
+ /** Generate npm package name variations from sanitized WESL identifier.
22588
+ *
22589
+ * Uses double-underscore encoding to distinguish scoped vs unscoped packages:
22590
+ * - Has __ → scoped package (try @scope/pkg variants)
22591
+ * - No __ → unscoped package (try pkg variants)
22592
+ *
22593
+ * Examples:
22594
+ * "lygia__shader_utils" → ["@lygia/shader_utils", "@lygia/shader-utils"]
22595
+ * "random_wgsl" → ["random_wgsl", "random-wgsl"]
22596
+ */
22597
+ function* npmNameVariations(sanitizedPath) {
22598
+ const [pkg, sub] = breakAt(sanitizedPath, "/");
22599
+ let pkgName = pkg;
22600
+ let scopePrefix = "";
22601
+ if (pkg.includes("__")) {
22602
+ const [scope, ...rest] = pkg.split("__");
22603
+ pkgName = rest.join("__");
22604
+ scopePrefix = `@${scope}/`;
22605
+ }
22606
+ yield `${scopePrefix}${pkgName}${sub}`;
22607
+ yield `${scopePrefix}${pkgName.replaceAll("_", "-")}${sub}`;
22608
+ }
22609
+ /** Break string at first occurrence of delimiter.
22610
+ * @returns [before, after] where after includes the delimiter */
22611
+ function breakAt(str, delimiter) {
22612
+ const index = str.indexOf(delimiter);
22613
+ if (index === -1) return [str, ""];
22614
+ return [str.slice(0, index), str.slice(index)];
22615
+ }
22616
+
22617
+ //#endregion
22618
+ //#region ../wesl/src/LinkedWesl.ts
22619
+ /**
22620
+ * Multiple WESL files that have been linked together to produce WGSL code.
22621
+ *
22622
+ * Call {@link LinkedWesl.createShaderModule} on a {@link WeslDevice}
22623
+ * to make the error reporting aware of the WESL code.
22624
+ */
22625
+ var LinkedWesl = class {
22626
+ sourceMap;
22627
+ constructor(sourceMap) {
22628
+ this.sourceMap = sourceMap;
22629
+ }
22630
+ /**
22631
+ * Creates a {@link GPUShaderModule}.
22632
+ * When errors occur, they will point at the original WESL source code.
22633
+ *
22634
+ * The compilation info {@link GPUShaderModule.getCompilationInfo}
22635
+ * can be remapped with {@link mapGPUCompilationInfo}
22636
+ * @param device GPUDevice. Preferably a {@link WeslDevice} for better error reporting.
22637
+ * @param descriptor - Description of the {@link GPUShaderModule} to create.
22638
+ */
22639
+ createShaderModule(device, descriptor) {
22640
+ if (!("injectError" in device)) return device.createShaderModule({
22641
+ ...descriptor,
22642
+ code: this.dest
22643
+ });
22644
+ device.pushErrorScope("validation");
22645
+ const module = device.createShaderModule({
22646
+ ...descriptor,
22647
+ code: this.dest
22648
+ });
22649
+ device.popErrorScope();
22650
+ const { promise, resolve } = Promise.withResolvers();
22651
+ device.injectError("validation", promise);
22652
+ module.getCompilationInfo().then((compilationInfo) => {
22653
+ if (compilationInfo.messages.length === 0) {
22654
+ resolve(null);
22655
+ return;
22656
+ }
22657
+ const mappedCompilationInfo = this.mapGPUCompilationInfo(compilationInfo);
22658
+ const errorMessage = compilationInfoToErrorMessage(mappedCompilationInfo, module);
22659
+ assertThatDebug(errorMessage !== null);
22660
+ const error = new GPUValidationError(errorMessage);
22661
+ error.cause = /* @__PURE__ */ new Error("createShaderModule failed");
22662
+ error.compilationInfo = mappedCompilationInfo;
22663
+ resolve(error);
22664
+ });
22665
+ return module;
22666
+ }
22667
+ /**
22668
+ * Use {@link LinkedWesl.createShaderModule} for a
22669
+ * better error reporting experience.
22670
+ */
22671
+ get dest() {
22672
+ return this.sourceMap.dest.text;
22673
+ }
22674
+ /** Turns raw compilation info into compilation info
22675
+ * that points at the WESL sources. */
22676
+ mapGPUCompilationInfo(compilationInfo) {
22677
+ return {
22678
+ __brand: compilationInfo.__brand,
22679
+ messages: compilationInfo.messages.map((v) => this.mapGPUCompilationMessage(v))
22680
+ };
22681
+ }
22682
+ mapGPUCompilationMessage(message) {
22683
+ const srcMap = this.sourceMap;
22684
+ const srcPosition = srcMap.destToSrc(message.offset);
22685
+ const length = (message.length > 0 ? srcMap.destToSrc(message.offset + message.length) : srcPosition).position - srcPosition.position;
22686
+ const [lineNum, linePos] = offsetToLineNumber(srcPosition.position, srcPosition.src.text);
22687
+ return {
22688
+ __brand: message.__brand,
22689
+ type: message.type,
22690
+ message: message.message,
22691
+ offset: srcPosition.position,
22692
+ length,
22693
+ lineNum,
22694
+ linePos,
22695
+ module: {
22696
+ url: srcPosition.src.path ?? "",
22697
+ text: srcPosition.src.text
22698
+ }
22699
+ };
22700
+ }
22701
+ };
22702
+ /**
22703
+ * Tries to imitate the way the browser logs the compilation info.
22704
+ * Does not do the remapping.
22705
+ * @returns A string with errors, or `null` if there were no compilation messages.
22706
+ */
22707
+ function compilationInfoToErrorMessage(compilationInfo, shaderModule) {
22708
+ if (compilationInfo.messages.length === 0) return null;
22709
+ let result = `Compilation log for [Invalid ShaderModule (${shaderModule.label || "unlabled"})]:\n`;
22710
+ const errorCount = compilationInfo.messages.filter((v) => v.type === "error").length;
22711
+ if (errorCount > 0) result += `${errorCount} error(s) generated while compiling the shader:\n`;
22712
+ for (const message of compilationInfo.messages) {
22713
+ const { lineNum, linePos } = message;
22714
+ result += `${message.module.url}:${lineNum}:${linePos}`;
22715
+ result += ` ${message.type}: ${message.message}\n`;
22716
+ const source = message.module.text;
22717
+ if (source) result += errorHighlight(source, [message.offset, message.offset + message.length]).join("\n");
22718
+ }
22719
+ return result;
22720
+ }
22721
+
22730
22722
  //#endregion
22731
22723
  //#region ../wesl/src/SrcMap.ts
22732
22724
  /** map text ranges in multiple src texts to a single dest text */
@@ -28092,13 +28084,14 @@ var WgslEdit = class extends HTMLElement {
28092
28084
  _lineNumbers = false;
28093
28085
  _files = /* @__PURE__ */ new Map();
28094
28086
  _activeFile = "";
28087
+ _rootModuleName;
28095
28088
  _tabs = true;
28096
28089
  _lint = "on";
28097
28090
  _fetchLibs = true;
28098
28091
  _conditions = {};
28099
28092
  _packageName;
28100
28093
  _libs = [];
28101
- _ignorePackages = ["constants", "test"];
28094
+ _ignorePackages = ["constants", "env"];
28102
28095
  _fetchingPkgs = /* @__PURE__ */ new Set();
28103
28096
  _fetchedPkgs = /* @__PURE__ */ new Set();
28104
28097
  _snackTimer;
@@ -28212,16 +28205,18 @@ var WgslEdit = class extends HTMLElement {
28212
28205
  if (conditions !== void 0) this._conditions = conditions;
28213
28206
  if (packageName !== void 0) this._packageName = packageName;
28214
28207
  if (libs !== void 0) this._libs = libs;
28208
+ if (rootModuleName !== void 0) this._rootModuleName = rootModuleName;
28215
28209
  if (weslSrc) {
28216
28210
  this.sources = weslSrc;
28217
- if (rootModuleName) this.activeFile = toTabName(rootModuleName);
28211
+ const tab = toTabName(rootModuleName ?? this._rootModuleName ?? "");
28212
+ if (tab) this.activeFile = tab;
28218
28213
  }
28219
28214
  this.updateLint();
28220
28215
  }
28221
28216
  /** Link/compile WESL sources into WGSL. Returns the compiled WGSL string. */
28222
28217
  async link(options) {
28223
28218
  const pkg = this._packageName ?? "package";
28224
- const rootModuleName = fileToModulePath(this._activeFile, pkg, false);
28219
+ const rootModuleName = this._rootModuleName ?? fileToModulePath(this._activeFile, pkg, false);
28225
28220
  return (await link({
28226
28221
  weslSrc: this.sources,
28227
28222
  rootModuleName,
@@ -28231,7 +28226,19 @@ var WgslEdit = class extends HTMLElement {
28231
28226
  ...options
28232
28227
  })).dest;
28233
28228
  }
28234
- /** Currently active file name. */
28229
+ /** Library bundles for linking (set via project). */
28230
+ get libs() {
28231
+ return this._libs;
28232
+ }
28233
+ /** Root module for linking (stable across tab switches). */
28234
+ get rootModuleName() {
28235
+ return this._rootModuleName;
28236
+ }
28237
+ set rootModuleName(value) {
28238
+ this._rootModuleName = value;
28239
+ this.dispatchChange();
28240
+ }
28241
+ /** Currently active file name (selected tab). */
28235
28242
  get activeFile() {
28236
28243
  return this._activeFile;
28237
28244
  }
@@ -28379,11 +28386,11 @@ var WgslEdit = class extends HTMLElement {
28379
28386
  }
28380
28387
  }
28381
28388
  dispatchChange() {
28382
- const { source, sources, conditions, _activeFile: activeFile } = this;
28389
+ const { source, sources, conditions, _rootModuleName: rootModuleName } = this;
28383
28390
  const detail = {
28384
28391
  source,
28385
28392
  sources,
28386
- activeFile,
28393
+ rootModuleName,
28387
28394
  conditions
28388
28395
  };
28389
28396
  this.dispatchEvent(new CustomEvent("change", { detail }));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wgsl-edit",
3
3
  "description": "Web component for editing WGSL/WESL with CodeMirror",
4
- "version": "0.0.18",
4
+ "version": "0.0.20",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -36,9 +36,9 @@
36
36
  "@codemirror/view": "^6.0.0",
37
37
  "@lezer/highlight": "^1.2.1",
38
38
  "codemirror": "^6.0.0",
39
- "lezer-wesl": "0.6.43",
40
- "wesl": "0.7.22",
41
- "wesl-fetch": "0.0.10"
39
+ "lezer-wesl": "0.6.44",
40
+ "wesl": "0.7.23",
41
+ "wesl-fetch": "0.0.11"
42
42
  },
43
43
  "devDependencies": {
44
44
  "open": "^10.0.0",
package/src/WgslEdit.ts CHANGED
@@ -139,13 +139,14 @@ export class WgslEdit extends HTMLElement {
139
139
 
140
140
  private _files: Map<string, FileState> = new Map();
141
141
  private _activeFile = "";
142
+ private _rootModuleName: string | undefined;
142
143
  private _tabs = true;
143
144
  private _lint: LintMode = "on";
144
145
  private _fetchLibs = true;
145
146
  private _conditions: Conditions = {};
146
147
  private _packageName: string | undefined;
147
148
  private _libs: WeslBundle[] = [];
148
- private _ignorePackages: string[] = ["constants", "test"];
149
+ private _ignorePackages: string[] = ["constants", "env"];
149
150
  private _fetchingPkgs = new Set<string>();
150
151
  private _fetchedPkgs = new Set<string>();
151
152
  private _snackTimer: ReturnType<typeof setTimeout> | undefined;
@@ -279,10 +280,12 @@ export class WgslEdit extends HTMLElement {
279
280
  if (conditions !== undefined) this._conditions = conditions;
280
281
  if (packageName !== undefined) this._packageName = packageName;
281
282
  if (libs !== undefined) this._libs = libs;
283
+ if (rootModuleName !== undefined) this._rootModuleName = rootModuleName;
282
284
 
283
285
  if (weslSrc) {
284
286
  this.sources = weslSrc;
285
- if (rootModuleName) this.activeFile = toTabName(rootModuleName);
287
+ const tab = toTabName(rootModuleName ?? this._rootModuleName ?? "");
288
+ if (tab) this.activeFile = tab;
286
289
  }
287
290
  this.updateLint();
288
291
  }
@@ -290,7 +293,8 @@ export class WgslEdit extends HTMLElement {
290
293
  /** Link/compile WESL sources into WGSL. Returns the compiled WGSL string. */
291
294
  async link(options?: Partial<LinkParams>): Promise<string> {
292
295
  const pkg = this._packageName ?? "package";
293
- const rootModuleName = fileToModulePath(this._activeFile, pkg, false);
296
+ const rootModuleName =
297
+ this._rootModuleName ?? fileToModulePath(this._activeFile, pkg, false);
294
298
  const linked = await link({
295
299
  weslSrc: this.sources,
296
300
  rootModuleName,
@@ -302,7 +306,22 @@ export class WgslEdit extends HTMLElement {
302
306
  return linked.dest;
303
307
  }
304
308
 
305
- /** Currently active file name. */
309
+ /** Library bundles for linking (set via project). */
310
+ get libs(): WeslBundle[] {
311
+ return this._libs;
312
+ }
313
+
314
+ /** Root module for linking (stable across tab switches). */
315
+ get rootModuleName(): string | undefined {
316
+ return this._rootModuleName;
317
+ }
318
+
319
+ set rootModuleName(value: string | undefined) {
320
+ this._rootModuleName = value;
321
+ this.dispatchChange();
322
+ }
323
+
324
+ /** Currently active file name (selected tab). */
306
325
  get activeFile(): string {
307
326
  return this._activeFile;
308
327
  }
@@ -474,8 +493,13 @@ export class WgslEdit extends HTMLElement {
474
493
  }
475
494
 
476
495
  private dispatchChange(): void {
477
- const { source, sources, conditions, _activeFile: activeFile } = this;
478
- const detail = { source, sources, activeFile, conditions };
496
+ const {
497
+ source,
498
+ sources,
499
+ conditions,
500
+ _rootModuleName: rootModuleName,
501
+ } = this;
502
+ const detail = { source, sources, rootModuleName, conditions };
479
503
  this.dispatchEvent(new CustomEvent("change", { detail }));
480
504
  }
481
505