wesl 0.6.15 → 0.6.17
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 +1 -1
- package/dist/index.d.ts +121 -33
- package/dist/index.js +296 -203
- package/package.json +3 -3
- package/src/BindIdents.ts +97 -118
- package/src/Linker.ts +149 -77
- package/src/ModuleResolver.ts +233 -0
- package/src/index.ts +2 -1
- package/src/test/BindWESL.test.ts +9 -15
- package/src/test/ErrorLogging.test.ts +16 -0
- package/src/test/TestUtil.ts +19 -28
- package/src/test/TransformBindingStructs.test.ts +8 -5
- package/src/ParsedRegistry.ts +0 -115
package/dist/index.js
CHANGED
|
@@ -976,6 +976,11 @@ function makeLiveDecls(parent = null) {
|
|
|
976
976
|
parent
|
|
977
977
|
};
|
|
978
978
|
}
|
|
979
|
+
/** debug routine for logging LiveDecls */
|
|
980
|
+
function liveDeclsToString(liveDecls) {
|
|
981
|
+
const { decls, parent } = liveDecls;
|
|
982
|
+
return `decls: { ${Array.from(decls.entries()).map(([name$1, decl]) => `${name$1}:${identToString(decl)}`).join(", ")} }, parent: ${parent ? liveDeclsToString(parent) : "null"}`;
|
|
983
|
+
}
|
|
979
984
|
|
|
980
985
|
//#endregion
|
|
981
986
|
//#region src/Mangler.ts
|
|
@@ -2306,63 +2311,46 @@ function stdEnumerant(name$1) {
|
|
|
2306
2311
|
* @return any new declaration elements found (they will need to be emitted)
|
|
2307
2312
|
*/
|
|
2308
2313
|
function bindIdents(params) {
|
|
2309
|
-
const { rootAst,
|
|
2314
|
+
const { rootAst, resolver, virtuals, accumulateUnbound } = params;
|
|
2310
2315
|
const { conditions = {}, mangler = minimalMangle } = params;
|
|
2311
|
-
const
|
|
2312
|
-
const globalNames
|
|
2313
|
-
const knownDecls = /* @__PURE__ */ new Set();
|
|
2314
|
-
const validRootDecls = findValidRootDecls(rootScope, conditions);
|
|
2315
|
-
validRootDecls.forEach((decl) => {
|
|
2316
|
-
decl.mangledName = decl.originalName;
|
|
2317
|
-
globalNames.add(decl.originalName);
|
|
2318
|
-
knownDecls.add(decl);
|
|
2319
|
-
});
|
|
2320
|
-
const unbound = accumulateUnbound ? [] : void 0;
|
|
2321
|
-
const globalStatements = /* @__PURE__ */ new Map();
|
|
2316
|
+
const validRootDecls = findValidRootDecls(rootAst.rootScope, conditions);
|
|
2317
|
+
const { globalNames, knownDecls } = initializeRootDecls(validRootDecls);
|
|
2322
2318
|
const bindContext = {
|
|
2323
|
-
|
|
2319
|
+
resolver,
|
|
2324
2320
|
conditions,
|
|
2325
2321
|
knownDecls,
|
|
2326
|
-
foundScopes: /* @__PURE__ */ new Set(),
|
|
2327
|
-
globalNames,
|
|
2328
|
-
globalStatements,
|
|
2329
2322
|
virtuals,
|
|
2330
2323
|
mangler,
|
|
2331
|
-
|
|
2324
|
+
foundScopes: /* @__PURE__ */ new Set(),
|
|
2325
|
+
globalNames,
|
|
2326
|
+
globalStatements: /* @__PURE__ */ new Map(),
|
|
2327
|
+
unbound: accumulateUnbound ? [] : void 0
|
|
2332
2328
|
};
|
|
2333
2329
|
const declEntries = validRootDecls.map((d) => [d.originalName, d]);
|
|
2330
|
+
const liveDecls = {
|
|
2331
|
+
decls: new Map(declEntries),
|
|
2332
|
+
parent: null
|
|
2333
|
+
};
|
|
2334
2334
|
return {
|
|
2335
|
-
decls: bindIdentsRecursive(rootScope, bindContext,
|
|
2336
|
-
decls: new Map(declEntries),
|
|
2337
|
-
parent: null
|
|
2338
|
-
}, true),
|
|
2335
|
+
decls: bindIdentsRecursive(rootAst.rootScope, bindContext, liveDecls, true),
|
|
2339
2336
|
globalNames,
|
|
2340
|
-
newStatements: [...globalStatements.values()],
|
|
2341
|
-
unbound
|
|
2337
|
+
newStatements: [...bindContext.globalStatements.values()],
|
|
2338
|
+
unbound: bindContext.unbound
|
|
2342
2339
|
};
|
|
2343
2340
|
}
|
|
2344
|
-
/**
|
|
2345
|
-
function
|
|
2346
|
-
const
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
globalStatements: /* @__PURE__ */ new Map(),
|
|
2353
|
-
mangler: minimalMangle,
|
|
2354
|
-
unbound: [],
|
|
2355
|
-
dontFollowDecls: true
|
|
2356
|
-
};
|
|
2357
|
-
Object.entries(registry.modules).forEach(([_module, ast]) => {
|
|
2358
|
-
const declEntries = findValidRootDecls(ast.rootScope, {}).map((d) => [d.originalName, d]);
|
|
2359
|
-
const liveDecls = {
|
|
2360
|
-
decls: new Map(declEntries),
|
|
2361
|
-
parent: null
|
|
2362
|
-
};
|
|
2363
|
-
bindIdentsRecursive(ast.rootScope, bindContext, liveDecls, true);
|
|
2341
|
+
/** Initialize root declarations with mangled names and add to tracking sets */
|
|
2342
|
+
function initializeRootDecls(validRootDecls) {
|
|
2343
|
+
const globalNames = /* @__PURE__ */ new Set();
|
|
2344
|
+
const knownDecls = /* @__PURE__ */ new Set();
|
|
2345
|
+
validRootDecls.forEach((decl) => {
|
|
2346
|
+
decl.mangledName = decl.originalName;
|
|
2347
|
+
globalNames.add(decl.originalName);
|
|
2348
|
+
knownDecls.add(decl);
|
|
2364
2349
|
});
|
|
2365
|
-
return
|
|
2350
|
+
return {
|
|
2351
|
+
globalNames,
|
|
2352
|
+
knownDecls
|
|
2353
|
+
};
|
|
2366
2354
|
}
|
|
2367
2355
|
/**
|
|
2368
2356
|
* Find all conditionally valid declarations at the root level.
|
|
@@ -2384,13 +2372,13 @@ function findValidRootDecls(rootScope, conditions) {
|
|
|
2384
2372
|
let elseValid = false;
|
|
2385
2373
|
for (const item of rootScope.contents) if (item.kind === "decl") {
|
|
2386
2374
|
assertThatDebug(item.declElem);
|
|
2387
|
-
const
|
|
2388
|
-
elseValid = nextElseState;
|
|
2389
|
-
if (valid) found.push(item);
|
|
2375
|
+
const validation = validateConditional(findConditional(item.declElem.attributes), elseValid, conditions);
|
|
2376
|
+
elseValid = validation.nextElseState;
|
|
2377
|
+
if (validation.valid) found.push(item);
|
|
2390
2378
|
} else if (item.kind === "partial") {
|
|
2391
|
-
const
|
|
2392
|
-
elseValid = nextElseState;
|
|
2393
|
-
if (valid) collectDecls(item, found);
|
|
2379
|
+
const validation = validateConditional(item.condAttribute, elseValid, conditions);
|
|
2380
|
+
elseValid = validation.nextElseState;
|
|
2381
|
+
if (validation.valid) collectDecls(item, found);
|
|
2394
2382
|
}
|
|
2395
2383
|
return found;
|
|
2396
2384
|
}
|
|
@@ -2431,14 +2419,22 @@ function bindIdentsRecursive(scope, bindContext, liveDecls, isRoot = false) {
|
|
|
2431
2419
|
const { dontFollowDecls, foundScopes } = bindContext;
|
|
2432
2420
|
if (foundScopes.has(scope)) return [];
|
|
2433
2421
|
foundScopes.add(scope);
|
|
2422
|
+
const { newGlobals, newFromChildren } = processScope(scope, bindContext, liveDecls, isRoot);
|
|
2423
|
+
return [
|
|
2424
|
+
newGlobals,
|
|
2425
|
+
newFromChildren,
|
|
2426
|
+
dontFollowDecls ? [] : handleDecls(newGlobals, bindContext)
|
|
2427
|
+
].flat();
|
|
2428
|
+
}
|
|
2429
|
+
/** Process all identifiers and subscopes in this scope */
|
|
2430
|
+
function processScope(scope, bindContext, liveDecls, isRoot) {
|
|
2434
2431
|
const newGlobals = [];
|
|
2435
2432
|
const newFromChildren = [];
|
|
2436
2433
|
let elseValid = false;
|
|
2437
2434
|
scope.contents.forEach((child) => {
|
|
2438
|
-
|
|
2439
|
-
if (kind$1 === "decl") {
|
|
2435
|
+
if (child.kind === "decl") {
|
|
2440
2436
|
if (!isRoot) liveDecls.decls.set(child.originalName, child);
|
|
2441
|
-
} else if (kind
|
|
2437
|
+
} else if (child.kind === "ref") {
|
|
2442
2438
|
const newDecl = handleRef(child, liveDecls, bindContext);
|
|
2443
2439
|
if (newDecl) newGlobals.push(newDecl);
|
|
2444
2440
|
} else {
|
|
@@ -2449,24 +2445,21 @@ function bindIdentsRecursive(scope, bindContext, liveDecls, isRoot = false) {
|
|
|
2449
2445
|
}
|
|
2450
2446
|
}
|
|
2451
2447
|
});
|
|
2452
|
-
return
|
|
2448
|
+
return {
|
|
2453
2449
|
newGlobals,
|
|
2454
|
-
newFromChildren
|
|
2455
|
-
|
|
2456
|
-
].flat();
|
|
2450
|
+
newFromChildren
|
|
2451
|
+
};
|
|
2457
2452
|
}
|
|
2458
2453
|
/**
|
|
2459
|
-
* Trace references to their declarations
|
|
2460
|
-
*
|
|
2461
|
-
* mangle declarations
|
|
2462
|
-
* mark references as 'std' if they match a wgsl std function or type
|
|
2454
|
+
* Trace references to their declarations.
|
|
2455
|
+
* Mutates to mangle declarations and mark std references.
|
|
2463
2456
|
*
|
|
2464
2457
|
* @return the found declaration, or undefined if this ref has already been processed
|
|
2465
2458
|
*/
|
|
2466
2459
|
function handleRef(ident$1, liveDecls, bindContext) {
|
|
2467
|
-
const {
|
|
2460
|
+
const { resolver, conditions, unbound, virtuals } = bindContext;
|
|
2468
2461
|
if (ident$1.refersTo || ident$1.std) return;
|
|
2469
|
-
const foundDecl = findDeclInModule(ident$1, liveDecls) ?? findQualifiedImport(ident$1,
|
|
2462
|
+
const foundDecl = findDeclInModule(ident$1, liveDecls) ?? findQualifiedImport(ident$1, resolver, conditions, virtuals, unbound);
|
|
2470
2463
|
if (foundDecl) {
|
|
2471
2464
|
ident$1.refersTo = foundDecl.decl;
|
|
2472
2465
|
return handleNewDecl(ident$1, foundDecl, bindContext);
|
|
@@ -2519,7 +2512,7 @@ function handleNewDecl(refIdent$1, foundDecl, bindContext) {
|
|
|
2519
2512
|
return decl;
|
|
2520
2513
|
}
|
|
2521
2514
|
}
|
|
2522
|
-
/**
|
|
2515
|
+
/** Search earlier in the scope and parent scopes for a matching declaration */
|
|
2523
2516
|
function findDeclInModule(ident$1, liveDecls) {
|
|
2524
2517
|
const found = liveDecls.decls.get(ident$1.originalName);
|
|
2525
2518
|
if (found) return {
|
|
@@ -2532,25 +2525,27 @@ function findDeclInModule(ident$1, liveDecls) {
|
|
|
2532
2525
|
* Match a reference identifier to a declaration in another module via an import statement
|
|
2533
2526
|
* or via an inline qualified ident e.g. foo::bar().
|
|
2534
2527
|
*/
|
|
2535
|
-
function findQualifiedImport(refIdent$1,
|
|
2528
|
+
function findQualifiedImport(refIdent$1, resolver, conditions, virtuals, unbound) {
|
|
2536
2529
|
const flatImps = flatImports(refIdent$1.ast, conditions);
|
|
2537
2530
|
const identParts = refIdent$1.originalName.split("::");
|
|
2538
2531
|
const modulePathParts = matchingImport(identParts, flatImps) ?? qualifiedIdent(identParts);
|
|
2539
|
-
if (modulePathParts) {
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
}
|
|
2546
|
-
|
|
2532
|
+
if (!modulePathParts) {
|
|
2533
|
+
if (unbound) unbound.push(identParts);
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
const result = findExport(modulePathParts, refIdent$1.ast.srcModule, resolver, conditions, virtuals);
|
|
2537
|
+
if (!result) if (unbound) unbound.push(modulePathParts);
|
|
2538
|
+
else failIdent(refIdent$1, `module not found for '${modulePathParts.join("::")}'`);
|
|
2539
|
+
return result;
|
|
2540
|
+
}
|
|
2541
|
+
/** Find an import statement that matches a provided identifier */
|
|
2547
2542
|
function matchingImport(identParts, flatImports$1) {
|
|
2548
2543
|
for (const flat of flatImports$1) if (flat.importPath.at(-1) === identParts.at(0)) return [...flat.modulePath, ...identParts.slice(1)];
|
|
2549
2544
|
}
|
|
2550
2545
|
/** @return an exported root declIdent for the provided path */
|
|
2551
|
-
function findExport(modulePathParts, srcModule,
|
|
2546
|
+
function findExport(modulePathParts, srcModule, resolver, conditions = {}, virtuals) {
|
|
2552
2547
|
const modulePath = absoluteModulePath(modulePathParts, srcModule).slice(0, -1).join("::");
|
|
2553
|
-
const moduleAst =
|
|
2548
|
+
const moduleAst = resolver.resolveModule(modulePath) ?? virtualModule(modulePathParts[0], conditions, virtuals);
|
|
2554
2549
|
if (!moduleAst) return void 0;
|
|
2555
2550
|
const name$1 = last(modulePathParts);
|
|
2556
2551
|
const decl = publicDecl(moduleAst.rootScope, name$1, conditions);
|
|
@@ -2594,9 +2589,8 @@ function rootLiveDecls(decl, conditions) {
|
|
|
2594
2589
|
return liveDecls;
|
|
2595
2590
|
}
|
|
2596
2591
|
/**
|
|
2597
|
-
*
|
|
2598
|
-
*
|
|
2599
|
-
* Also update the set of globally unique names.
|
|
2592
|
+
* Set a globally unique mangled name for this declaration.
|
|
2593
|
+
* Uses the mangler function to avoid name collisions.
|
|
2600
2594
|
*/
|
|
2601
2595
|
function setMangledName(proposedName, decl, globalNames, srcModule, mangler) {
|
|
2602
2596
|
if (decl.mangledName) return;
|
|
@@ -2758,68 +2752,136 @@ function noSuffix(path) {
|
|
|
2758
2752
|
}
|
|
2759
2753
|
|
|
2760
2754
|
//#endregion
|
|
2761
|
-
//#region src/
|
|
2762
|
-
function parsedRegistry() {
|
|
2763
|
-
resetScopeIds();
|
|
2764
|
-
return { modules: {} };
|
|
2765
|
-
}
|
|
2766
|
-
/** for debug */
|
|
2767
|
-
function registryToString(registry) {
|
|
2768
|
-
return `modules: ${[...Object.keys(registry.modules)]}`;
|
|
2769
|
-
}
|
|
2770
|
-
/** Look up a module with a flexible selector.
|
|
2771
|
-
* :: separated module path, package::util
|
|
2772
|
-
* / separated file path ./util.wesl (or ./util)
|
|
2773
|
-
* - note: a file path should not include a weslRoot prefix, e.g. not ./shaders/util.wesl
|
|
2774
|
-
* simpleName util
|
|
2775
|
-
*/
|
|
2776
|
-
function selectModule(parsed, selectPath, packageName = "package") {
|
|
2777
|
-
let modulePath;
|
|
2778
|
-
if (selectPath.includes("::")) modulePath = selectPath;
|
|
2779
|
-
else if (selectPath.includes("/") || selectPath.endsWith(".wesl") || selectPath.endsWith(".wgsl")) modulePath = fileToModulePath(selectPath, packageName);
|
|
2780
|
-
else modulePath = packageName + "::" + selectPath;
|
|
2781
|
-
return parsed.modules[modulePath];
|
|
2782
|
-
}
|
|
2783
|
-
/**
|
|
2784
|
-
* @param srcFiles map of source strings by file path
|
|
2785
|
-
* key is '/' separated relative path (relative to srcRoot, not absolute file path )
|
|
2786
|
-
* value is wesl source string
|
|
2787
|
-
* @param registry add parsed modules to this registry
|
|
2788
|
-
* @param packageName name of package
|
|
2789
|
-
*/
|
|
2790
|
-
function parseIntoRegistry(srcFiles, registry, packageName = "package", debugWeslRoot) {
|
|
2791
|
-
let weslRoot$1 = debugWeslRoot;
|
|
2792
|
-
if (weslRoot$1 === void 0) weslRoot$1 = "";
|
|
2793
|
-
else if (!weslRoot$1.endsWith("/")) weslRoot$1 += "/";
|
|
2794
|
-
Object.entries(srcFiles).map(([filePath, src]) => {
|
|
2795
|
-
return {
|
|
2796
|
-
modulePath: fileToModulePath(filePath, packageName),
|
|
2797
|
-
debugFilePath: weslRoot$1 + filePath,
|
|
2798
|
-
src
|
|
2799
|
-
};
|
|
2800
|
-
}).forEach((mod) => {
|
|
2801
|
-
const parsed = parseSrcModule(mod);
|
|
2802
|
-
if (registry.modules[mod.modulePath]) return;
|
|
2803
|
-
registry.modules[mod.modulePath] = parsed;
|
|
2804
|
-
});
|
|
2805
|
-
}
|
|
2806
|
-
function parseLibsIntoRegistry(libs, registry) {
|
|
2807
|
-
libs.forEach(({ modules, name: name$1 }) => {
|
|
2808
|
-
parseIntoRegistry(modules, registry, name$1);
|
|
2809
|
-
});
|
|
2810
|
-
libs.forEach(({ dependencies }) => {
|
|
2811
|
-
parseLibsIntoRegistry(dependencies || [], registry);
|
|
2812
|
-
});
|
|
2813
|
-
}
|
|
2755
|
+
//#region src/ModuleResolver.ts
|
|
2814
2756
|
const libRegex = /^lib\.w[eg]sl$/i;
|
|
2815
|
-
/**
|
|
2816
|
-
|
|
2817
|
-
|
|
2757
|
+
/** Module resolver for in-memory source records. Lazy by default. */
|
|
2758
|
+
var RecordResolver = class {
|
|
2759
|
+
astCache = /* @__PURE__ */ new Map();
|
|
2760
|
+
sources;
|
|
2761
|
+
packageName;
|
|
2762
|
+
debugWeslRoot;
|
|
2763
|
+
constructor(sources, options = {}) {
|
|
2764
|
+
const { packageName = "package", debugWeslRoot } = options;
|
|
2765
|
+
this.sources = sources;
|
|
2766
|
+
this.packageName = packageName;
|
|
2767
|
+
this.debugWeslRoot = normalizeDebugRoot(debugWeslRoot);
|
|
2768
|
+
}
|
|
2769
|
+
resolveModule(modulePath) {
|
|
2770
|
+
const cached = this.astCache.get(modulePath);
|
|
2771
|
+
if (cached) return cached;
|
|
2772
|
+
const source = this.findSource(modulePath);
|
|
2773
|
+
if (!source) return void 0;
|
|
2774
|
+
const ast = parseSrcModule({
|
|
2775
|
+
modulePath,
|
|
2776
|
+
debugFilePath: this.modulePathToDebugPath(modulePath),
|
|
2777
|
+
src: source
|
|
2778
|
+
});
|
|
2779
|
+
this.astCache.set(modulePath, ast);
|
|
2780
|
+
return ast;
|
|
2781
|
+
}
|
|
2782
|
+
findSource(modulePath) {
|
|
2783
|
+
if (this.sources[modulePath]) return this.sources[modulePath];
|
|
2784
|
+
const filePath = this.moduleToFilePath(modulePath);
|
|
2785
|
+
return findInVariants(this.sources, filePath);
|
|
2786
|
+
}
|
|
2787
|
+
moduleToFilePath(modulePath) {
|
|
2788
|
+
const parts = modulePath.split("::");
|
|
2789
|
+
if (parts[0] !== this.packageName && parts[0] !== "package") return modulePath;
|
|
2790
|
+
return parts.slice(1).join("/");
|
|
2791
|
+
}
|
|
2792
|
+
modulePathToDebugPath(modulePath) {
|
|
2793
|
+
const filePath = this.moduleToFilePath(modulePath);
|
|
2794
|
+
return this.debugWeslRoot + filePath + ".wesl";
|
|
2795
|
+
}
|
|
2796
|
+
/** Return all modules, parsing them on-demand if needed. */
|
|
2797
|
+
allModules() {
|
|
2798
|
+
for (const filePath of Object.keys(this.sources)) {
|
|
2799
|
+
const modulePath = fileToModulePath(filePath, this.packageName, this.packageName !== "package");
|
|
2800
|
+
this.resolveModule(modulePath);
|
|
2801
|
+
}
|
|
2802
|
+
return this.astCache.entries();
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
/** Composite resolver that tries each resolver in order until one succeeds. */
|
|
2806
|
+
var CompositeResolver = class {
|
|
2807
|
+
resolvers;
|
|
2808
|
+
constructor(resolvers) {
|
|
2809
|
+
this.resolvers = resolvers;
|
|
2810
|
+
}
|
|
2811
|
+
resolveModule(modulePath) {
|
|
2812
|
+
for (const resolver of this.resolvers) {
|
|
2813
|
+
const ast = resolver.resolveModule(modulePath);
|
|
2814
|
+
if (ast) return ast;
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
};
|
|
2818
|
+
/** Lazy resolver for WeslBundle library modules. */
|
|
2819
|
+
var BundleResolver = class {
|
|
2820
|
+
astCache = /* @__PURE__ */ new Map();
|
|
2821
|
+
sources;
|
|
2822
|
+
packageName;
|
|
2823
|
+
debugWeslRoot;
|
|
2824
|
+
constructor(bundle, debugWeslRoot) {
|
|
2825
|
+
this.sources = bundle.modules;
|
|
2826
|
+
this.packageName = bundle.name;
|
|
2827
|
+
this.debugWeslRoot = normalizeDebugRoot(debugWeslRoot);
|
|
2828
|
+
}
|
|
2829
|
+
resolveModule(modulePath) {
|
|
2830
|
+
if (modulePath !== this.packageName && !modulePath.startsWith(this.packageName + "::")) return;
|
|
2831
|
+
const cached = this.astCache.get(modulePath);
|
|
2832
|
+
if (cached) return cached;
|
|
2833
|
+
const source = this.findSource(modulePath);
|
|
2834
|
+
if (!source) return void 0;
|
|
2835
|
+
const ast = parseSrcModule({
|
|
2836
|
+
modulePath,
|
|
2837
|
+
debugFilePath: this.modulePathToDebugPath(modulePath),
|
|
2838
|
+
src: source
|
|
2839
|
+
});
|
|
2840
|
+
this.astCache.set(modulePath, ast);
|
|
2841
|
+
return ast;
|
|
2842
|
+
}
|
|
2843
|
+
findSource(modulePath) {
|
|
2844
|
+
const filePath = this.moduleToFilePath(modulePath);
|
|
2845
|
+
if (modulePath === this.packageName) {
|
|
2846
|
+
const libSrc = findInVariants(this.sources, "lib", ["wesl", "wgsl"]);
|
|
2847
|
+
if (libSrc) return libSrc;
|
|
2848
|
+
}
|
|
2849
|
+
return findInVariants(this.sources, filePath);
|
|
2850
|
+
}
|
|
2851
|
+
moduleToFilePath(modulePath) {
|
|
2852
|
+
const parts = modulePath.split("::");
|
|
2853
|
+
if (parts[0] !== this.packageName) return modulePath;
|
|
2854
|
+
return parts.slice(1).join("/");
|
|
2855
|
+
}
|
|
2856
|
+
modulePathToDebugPath(modulePath) {
|
|
2857
|
+
const filePath = this.moduleToFilePath(modulePath);
|
|
2858
|
+
return this.debugWeslRoot + this.packageName + "/" + filePath + ".wesl";
|
|
2859
|
+
}
|
|
2860
|
+
};
|
|
2861
|
+
/** Convert file path to module path (e.g., "foo/bar.wesl" -> "package::foo::bar"). */
|
|
2862
|
+
function fileToModulePath(filePath, packageName, treatLibAsRoot) {
|
|
2818
2863
|
if (filePath.includes("::")) return filePath;
|
|
2819
|
-
if (
|
|
2864
|
+
if (treatLibAsRoot && libRegex.test(filePath)) return packageName;
|
|
2820
2865
|
const moduleSuffix = noSuffix(normalize(filePath)).replaceAll("/", "::");
|
|
2821
2866
|
return packageName + "::" + moduleSuffix;
|
|
2822
2867
|
}
|
|
2868
|
+
/** Normalize debug root to end with / or be empty. */
|
|
2869
|
+
function normalizeDebugRoot(debugWeslRoot) {
|
|
2870
|
+
if (debugWeslRoot === void 0) return "./";
|
|
2871
|
+
if (debugWeslRoot === "") return "";
|
|
2872
|
+
return debugWeslRoot.endsWith("/") ? debugWeslRoot : debugWeslRoot + "/";
|
|
2873
|
+
}
|
|
2874
|
+
/** Try path variants with and without ./ prefix and extension suffixes. */
|
|
2875
|
+
function findInVariants(sources, basePath, extensions = ["wesl", "wgsl"]) {
|
|
2876
|
+
for (const prefix of ["", "./"]) {
|
|
2877
|
+
const path = prefix + basePath;
|
|
2878
|
+
if (sources[path]) return sources[path];
|
|
2879
|
+
for (const ext of extensions) {
|
|
2880
|
+
const withExt = `${path}.${ext}`;
|
|
2881
|
+
if (sources[withExt]) return sources[withExt];
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2823
2885
|
|
|
2824
2886
|
//#endregion
|
|
2825
2887
|
//#region src/Linker.ts
|
|
@@ -2833,25 +2895,47 @@ function fileToModulePath(filePath, packageName) {
|
|
|
2833
2895
|
* Only code that is valid with the current conditions is included in the output.
|
|
2834
2896
|
*/
|
|
2835
2897
|
async function link(params) {
|
|
2836
|
-
|
|
2837
|
-
const registry = parsedRegistry();
|
|
2838
|
-
parseIntoRegistry(weslSrc, registry, "package", debugWeslRoot);
|
|
2839
|
-
parseLibsIntoRegistry(libs, registry);
|
|
2840
|
-
return new LinkedWesl(linkRegistry({
|
|
2841
|
-
registry,
|
|
2842
|
-
...params
|
|
2843
|
-
}));
|
|
2898
|
+
return new LinkedWesl(_linkSync(params));
|
|
2844
2899
|
}
|
|
2845
2900
|
/** linker api for benchmarking */
|
|
2846
2901
|
function _linkSync(params) {
|
|
2847
|
-
const { weslSrc,
|
|
2848
|
-
const
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2902
|
+
const { weslSrc, libs = [], packageName, debugWeslRoot } = params;
|
|
2903
|
+
const { resolver } = params;
|
|
2904
|
+
const resolvers = [];
|
|
2905
|
+
if (resolver) resolvers.push(resolver);
|
|
2906
|
+
else if (weslSrc) resolvers.push(new RecordResolver(weslSrc, {
|
|
2907
|
+
packageName,
|
|
2908
|
+
debugWeslRoot
|
|
2854
2909
|
}));
|
|
2910
|
+
else throw new Error("Either resolver or weslSrc must be provided");
|
|
2911
|
+
if (libs.length > 0) {
|
|
2912
|
+
const libResolvers = createLibraryResolvers(libs, debugWeslRoot);
|
|
2913
|
+
resolvers.push(...libResolvers);
|
|
2914
|
+
}
|
|
2915
|
+
return linkRegistry({
|
|
2916
|
+
resolver: resolvers.length === 1 ? resolvers[0] : new CompositeResolver(resolvers),
|
|
2917
|
+
...params
|
|
2918
|
+
});
|
|
2919
|
+
}
|
|
2920
|
+
function createLibraryResolvers(libs, debugWeslRoot) {
|
|
2921
|
+
return flattenLibraryTree(libs).map((lib) => new BundleResolver(lib, debugWeslRoot));
|
|
2922
|
+
}
|
|
2923
|
+
/** Flatten library dependency tree, deduplicating by object identity rather than package name.
|
|
2924
|
+
*
|
|
2925
|
+
* Some packages (like Lygia) provide multiple bundles in the same npm package
|
|
2926
|
+
* to enable tree shaking. All bundles share the same package name, so we deduplicate
|
|
2927
|
+
* by object identity to keep them distinct. Also handles circular dependencies correctly. */
|
|
2928
|
+
function flattenLibraryTree(libs) {
|
|
2929
|
+
const result = [];
|
|
2930
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2931
|
+
function visit(bundle) {
|
|
2932
|
+
if (seen.has(bundle)) return;
|
|
2933
|
+
seen.add(bundle);
|
|
2934
|
+
result.push(bundle);
|
|
2935
|
+
bundle.dependencies?.forEach(visit);
|
|
2936
|
+
}
|
|
2937
|
+
libs.forEach(visit);
|
|
2938
|
+
return result;
|
|
2855
2939
|
}
|
|
2856
2940
|
/** Link wesl from a registry of already parsed modules.
|
|
2857
2941
|
*
|
|
@@ -2864,22 +2948,16 @@ function linkRegistry(params) {
|
|
|
2864
2948
|
const { transformedAst, newDecls, newStatements } = bindAndTransform(params);
|
|
2865
2949
|
return SrcMapBuilder.build(emitWgsl(transformedAst.moduleElem, transformedAst.srcModule, newDecls, newStatements, params.conditions));
|
|
2866
2950
|
}
|
|
2867
|
-
/**
|
|
2951
|
+
/** Bind identifiers and apply transform plugins */
|
|
2868
2952
|
function bindAndTransform(params) {
|
|
2869
|
-
const {
|
|
2953
|
+
const { resolver, mangler, constants, config } = params;
|
|
2870
2954
|
const { rootModuleName = "main", conditions = {} } = params;
|
|
2871
|
-
const rootAst = getRootModule(
|
|
2872
|
-
const { constants, config } = params;
|
|
2873
|
-
let { virtualLibs } = params;
|
|
2874
|
-
if (constants) virtualLibs = {
|
|
2875
|
-
...virtualLibs,
|
|
2876
|
-
constants: constantsGenerator(constants)
|
|
2877
|
-
};
|
|
2955
|
+
const rootAst = getRootModule(resolver, normalizeModuleName(rootModuleName), rootModuleName);
|
|
2878
2956
|
const { globalNames, decls: newDecls, newStatements } = bindIdents({
|
|
2879
2957
|
rootAst,
|
|
2880
|
-
|
|
2958
|
+
resolver,
|
|
2881
2959
|
conditions,
|
|
2882
|
-
virtuals:
|
|
2960
|
+
virtuals: setupVirtualLibs(params.virtualLibs, constants),
|
|
2883
2961
|
mangler
|
|
2884
2962
|
});
|
|
2885
2963
|
return {
|
|
@@ -2888,22 +2966,34 @@ function bindAndTransform(params) {
|
|
|
2888
2966
|
newStatements
|
|
2889
2967
|
};
|
|
2890
2968
|
}
|
|
2891
|
-
|
|
2892
|
-
|
|
2969
|
+
/** Convert root module name to module path format.
|
|
2970
|
+
* Accepts: module path (package::foo), file path (./foo.wesl), or name (foo) */
|
|
2971
|
+
function normalizeModuleName(name$1) {
|
|
2972
|
+
if (name$1.includes("::")) return name$1;
|
|
2973
|
+
if (name$1.includes("/") || name$1.endsWith(".wesl") || name$1.endsWith(".wgsl")) return "package::" + name$1.replace(/\.(wesl|wgsl)$/, "").replace(/^\.\//, "").replaceAll("/", "::");
|
|
2974
|
+
return "package::" + name$1;
|
|
2893
2975
|
}
|
|
2894
|
-
/**
|
|
2895
|
-
function getRootModule(
|
|
2896
|
-
const
|
|
2897
|
-
if (!
|
|
2898
|
-
if (tracing) {
|
|
2899
|
-
console.log(`parsed modules: ${Object.keys(parsed.modules)}`);
|
|
2900
|
-
console.log(`root module not found: ${rootModuleName}`);
|
|
2901
|
-
}
|
|
2976
|
+
/** Resolve root module AST or throw if not found. */
|
|
2977
|
+
function getRootModule(resolver, modulePath, rootModuleName) {
|
|
2978
|
+
const rootAst = resolver.resolveModule(modulePath);
|
|
2979
|
+
if (!rootAst) {
|
|
2980
|
+
if (tracing) console.log(`root module not found: ${modulePath} (from ${rootModuleName})`);
|
|
2902
2981
|
throw new Error(`Root module not found: ${rootModuleName}`);
|
|
2903
2982
|
}
|
|
2904
|
-
return
|
|
2983
|
+
return rootAst;
|
|
2984
|
+
}
|
|
2985
|
+
/** Create virtual library set from code generators and host constants. */
|
|
2986
|
+
function setupVirtualLibs(virtualLibs, constants) {
|
|
2987
|
+
let libs = virtualLibs;
|
|
2988
|
+
if (constants) {
|
|
2989
|
+
const constantsGen = () => Object.entries(constants).map(([name$1, value]) => `const ${name$1} = ${value};`).join("\n");
|
|
2990
|
+
libs = {
|
|
2991
|
+
...libs,
|
|
2992
|
+
constants: constantsGen
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
return libs && mapValues(libs, (fn$1) => ({ fn: fn$1 }));
|
|
2905
2996
|
}
|
|
2906
|
-
/** run any plugins that transform the AST */
|
|
2907
2997
|
function applyTransformPlugins(rootModule, globalNames, config) {
|
|
2908
2998
|
const { moduleElem, srcModule } = rootModule;
|
|
2909
2999
|
const startAst = {
|
|
@@ -2914,23 +3004,8 @@ function applyTransformPlugins(rootModule, globalNames, config) {
|
|
|
2914
3004
|
};
|
|
2915
3005
|
return filterMap(config?.plugins ?? [], (plugin) => plugin.transform).reduce((ast, transform) => transform(ast), startAst);
|
|
2916
3006
|
}
|
|
2917
|
-
/** traverse the AST and emit WGSL */
|
|
2918
3007
|
function emitWgsl(rootModuleElem, srcModule, newDecls, newStatements, conditions = {}) {
|
|
2919
|
-
const prologueBuilders = newStatements.map((s) =>
|
|
2920
|
-
const { elem, srcModule: srcModule$1 } = s;
|
|
2921
|
-
const { src: text$1, debugFilePath: path } = srcModule$1;
|
|
2922
|
-
const builder = new SrcMapBuilder({
|
|
2923
|
-
text: text$1,
|
|
2924
|
-
path
|
|
2925
|
-
});
|
|
2926
|
-
lowerAndEmit({
|
|
2927
|
-
srcBuilder: builder,
|
|
2928
|
-
rootElems: [elem],
|
|
2929
|
-
conditions
|
|
2930
|
-
});
|
|
2931
|
-
builder.addNl();
|
|
2932
|
-
return builder;
|
|
2933
|
-
});
|
|
3008
|
+
const prologueBuilders = newStatements.map((s) => emitStatement(s, conditions));
|
|
2934
3009
|
const rootBuilder = new SrcMapBuilder({
|
|
2935
3010
|
text: srcModule.src,
|
|
2936
3011
|
path: srcModule.debugFilePath
|
|
@@ -2941,25 +3016,43 @@ function emitWgsl(rootModuleElem, srcModule, newDecls, newStatements, conditions
|
|
|
2941
3016
|
conditions,
|
|
2942
3017
|
extracting: false
|
|
2943
3018
|
});
|
|
2944
|
-
const declBuilders = newDecls.map((decl) =>
|
|
2945
|
-
const builder = new SrcMapBuilder({
|
|
2946
|
-
text: decl.srcModule.src,
|
|
2947
|
-
path: decl.srcModule.debugFilePath
|
|
2948
|
-
});
|
|
2949
|
-
lowerAndEmit({
|
|
2950
|
-
srcBuilder: builder,
|
|
2951
|
-
rootElems: [decl.declElem],
|
|
2952
|
-
conditions,
|
|
2953
|
-
skipConditionalFiltering: true
|
|
2954
|
-
});
|
|
2955
|
-
return builder;
|
|
2956
|
-
});
|
|
3019
|
+
const declBuilders = newDecls.map((decl) => emitDecl(decl, conditions));
|
|
2957
3020
|
return [
|
|
2958
3021
|
...prologueBuilders,
|
|
2959
3022
|
rootBuilder,
|
|
2960
3023
|
...declBuilders
|
|
2961
3024
|
];
|
|
2962
3025
|
}
|
|
3026
|
+
function emitStatement(s, conditions) {
|
|
3027
|
+
const { elem, srcModule } = s;
|
|
3028
|
+
const { src: text$1, debugFilePath: path } = srcModule;
|
|
3029
|
+
const builder = new SrcMapBuilder({
|
|
3030
|
+
text: text$1,
|
|
3031
|
+
path
|
|
3032
|
+
});
|
|
3033
|
+
lowerAndEmit({
|
|
3034
|
+
srcBuilder: builder,
|
|
3035
|
+
rootElems: [elem],
|
|
3036
|
+
conditions
|
|
3037
|
+
});
|
|
3038
|
+
builder.addNl();
|
|
3039
|
+
return builder;
|
|
3040
|
+
}
|
|
3041
|
+
/** Skip conditional filtering because findValidRootDecls already validated these declarations */
|
|
3042
|
+
function emitDecl(decl, conditions) {
|
|
3043
|
+
const { src: text$1, debugFilePath: path } = decl.srcModule;
|
|
3044
|
+
const builder = new SrcMapBuilder({
|
|
3045
|
+
text: text$1,
|
|
3046
|
+
path
|
|
3047
|
+
});
|
|
3048
|
+
lowerAndEmit({
|
|
3049
|
+
srcBuilder: builder,
|
|
3050
|
+
rootElems: [decl.declElem],
|
|
3051
|
+
conditions,
|
|
3052
|
+
skipConditionalFiltering: true
|
|
3053
|
+
});
|
|
3054
|
+
return builder;
|
|
3055
|
+
}
|
|
2963
3056
|
|
|
2964
3057
|
//#endregion
|
|
2965
3058
|
//#region src/LinkerUtil.ts
|
|
@@ -3294,4 +3387,4 @@ function makeWeslDevice(device) {
|
|
|
3294
3387
|
}
|
|
3295
3388
|
|
|
3296
3389
|
//#endregion
|
|
3297
|
-
export { LinkedWesl, WeslParseError, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindingStructsPlugin, blankWeslParseState, childIdent, childScope, containsScope, debugContentsToString, emptyScope, errorHighlight, filterMap, findMap, findRefsToBindingStructs,
|
|
3390
|
+
export { BundleResolver, CompositeResolver, LinkedWesl, RecordResolver, WeslParseError, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, blankWeslParseState, childIdent, childScope, containsScope, debugContentsToString, emptyScope, errorHighlight, filterMap, findMap, findRefsToBindingStructs, findValidRootDecls, flatImports, groupBy, grouped, identToString, isGlobal, last, lengthPrefixMangle, link, linkRegistry, liveDeclsToString, lowerBindingStructs, makeLiveDecls, makeWeslDevice, mapForward, mapValues, markBindingStructs, markEntryTypes, mergeScope, minimalMangle, minimallyMangledName, multiKeySet, nextIdentId, noSuffix, normalize, normalizeModuleName, offsetToLineNumber, overlapTail, parseSrcModule, partition, publicDecl, replaceWords, requestWeslDevice, resetScopeIds, scan, scopeToString, scopeToStringLong, syntheticWeslParseState, transformBindingReference, transformBindingStruct, underscoreMangle };
|