wesl-tooling 0.6.21 → 0.6.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,186 @@
1
+ import { ModuleResolver, WeslAST, WeslBundle } from "wesl";
2
+
3
+ //#region src/FileModuleResolver.d.ts
4
+
5
+ /**
6
+ * Loads WESL modules from the filesystem on demand with caching.
7
+ *
8
+ * Resolves module paths like `package::foo::bar` to filesystem paths
9
+ * like `baseDir/foo/bar.wesl` or `baseDir/foo/bar.wgsl`.
10
+ */
11
+ declare class FileModuleResolver implements ModuleResolver {
12
+ /** Cached parsed ASTs to avoid re-parsing the same module */
13
+ readonly astCache: Map<string, WeslAST>;
14
+ /** Root directory containing shader source files */
15
+ readonly baseDir: string;
16
+ /** Package name that this resolver handles (in addition to generic "package") */
17
+ readonly packageName: string;
18
+ /** Optional root path for debug file paths (for browser-clickable errors) */
19
+ readonly debugWeslRoot?: string;
20
+ /**
21
+ * @param baseDir - Root directory containing shader source files
22
+ * @param packageName - Package name to resolve (defaults to "package")
23
+ * @param debugWeslRoot - Optional root path for debug file paths. If provided, error messages
24
+ * will show paths relative to this root (e.g., "shaders/foo.wesl") instead of absolute
25
+ * filesystem paths. This is needed for clickable errors in browser dev tools.
26
+ */
27
+ constructor(baseDir: string, packageName?: string, debugWeslRoot?: string);
28
+ /**
29
+ * Resolves and parses a module by its import path.
30
+ *
31
+ * Returns cached AST if available, otherwise loads from filesystem,
32
+ * parses, caches, and returns the AST. Returns undefined if module
33
+ * cannot be found.
34
+ *
35
+ * @param modulePath - Module path like "package::foo::bar"
36
+ * @returns Parsed AST or undefined if module not found
37
+ */
38
+ resolveModule(modulePath: string): WeslAST | undefined;
39
+ /** Try .wesl first, then .wgsl */
40
+ private tryExtensions;
41
+ private loadSource;
42
+ /** Convert module path (package::foo::bar) to filesystem path (baseDir/foo/bar) */
43
+ private moduleToFilePath;
44
+ /** Convert module path to debug path for error messages */
45
+ private modulePathToDebugPath;
46
+ }
47
+ //#endregion
48
+ //#region src/LoadModules.d.ts
49
+ /**
50
+ * Load the wesl/wgsl shader sources.
51
+ *
52
+ * If baseDir or srcGlob are not provided, this function will attempt to read
53
+ * configuration from wesl.toml in the projectDir. If no wesl.toml exists,
54
+ * default values will be used.
55
+ *
56
+ * @param projectDir The project directory (typically cwd or directory containing package.json)
57
+ * @param baseDir Optional base directory for shaders (overrides wesl.toml if provided)
58
+ * @param srcGlob Optional glob pattern for shader files (overrides wesl.toml if provided)
59
+ */
60
+ declare function loadModules(projectDir: string, baseDir?: string, srcGlob?: string): Promise<Record<string, string>>;
61
+ declare function zip<A, B>(as: A[], bs: B[]): [A, B][];
62
+ //#endregion
63
+ //#region src/LoadProject.d.ts
64
+ interface ProjectInfo {
65
+ weslSrc: Record<string, string>;
66
+ rootModuleName: string;
67
+ packageName: string;
68
+ libs: WeslBundle[];
69
+ }
70
+ interface LoadProjectOptions {
71
+ /** Libraries provided at runtime, don't resolve from npm (e.g., ["test"]) */
72
+ virtualLibs?: string[];
73
+ }
74
+ /**
75
+ * Load everything needed to link a shader file.
76
+ *
77
+ * Discovers the project root, loads wesl.toml config, reads all shader modules,
78
+ * and resolves external library dependencies from npm.
79
+ *
80
+ * @returns ProjectInfo with sources, libs, and module paths, or null if not in a project.
81
+ */
82
+ declare function loadProject(filePath: string, opts?: LoadProjectOptions): Promise<ProjectInfo | null>;
83
+ /** Read package name from package.json, sanitized for WESL identifiers. */
84
+ declare function getPackageName(projectDir: string): Promise<string>;
85
+ //#endregion
86
+ //#region src/LoadWeslToml.d.ts
87
+ /** Configuration from wesl.toml */
88
+ interface WeslToml {
89
+ /** WESL edition (e.g. "unstable_2025") */
90
+ edition: string;
91
+ /** glob patterns to find .wesl/.wgsl files. Relative to the toml directory. */
92
+ include: string[];
93
+ /** base directory for wesl files. Relative to the toml directory. */
94
+ root: string;
95
+ /** glob patterns to exclude directories. */
96
+ exclude?: string[];
97
+ /** package manager ("npm" or "cargo") */
98
+ "package-manager"?: string;
99
+ /** names of directly referenced wesl shader packages (e.g. npm package names), or "auto" for auto-detection */
100
+ dependencies?: string[] | string;
101
+ }
102
+ /** Information about the loaded wesl.toml file and its location */
103
+ interface WeslTomlInfo {
104
+ /** The path to the toml file, relative to the cwd, undefined if no toml file */
105
+ tomlFile: string | undefined;
106
+ /** The absolute path to the directory that contains the toml.
107
+ * Paths inside the toml are relative to this. */
108
+ tomlDir: string;
109
+ /** The wesl root, relative to the projectDir.
110
+ * This lets loadModules do `path.resolve(projectDir, resolvedRoot)` */
111
+ resolvedRoot: string;
112
+ /** The underlying toml file */
113
+ toml: WeslToml;
114
+ }
115
+ /** Default configuration when no wesl.toml is found */
116
+ declare const defaultWeslToml: WeslToml;
117
+ /**
118
+ * Load and parse a wesl.toml file from the fs.
119
+ * Provide default values for any required WeslToml fields.
120
+ */
121
+ declare function loadWeslToml(tomlFile: string): Promise<WeslToml>;
122
+ /**
123
+ * Find and load the wesl.toml file, or use defaults if not found
124
+ *
125
+ * @param projectDir The directory to search for wesl.toml (typically cwd or project root)
126
+ * @param specifiedToml Optional explicit path to a toml file
127
+ * @returns Information about the loaded TOML configuration
128
+ */
129
+ declare function findWeslToml(projectDir: string, specifiedToml?: string): Promise<WeslTomlInfo>;
130
+ //#endregion
131
+ //#region src/ParseDependencies.d.ts
132
+ /**
133
+ * Find package dependencies in WESL source files.
134
+ *
135
+ * Parses sources and partially binds identifiers to reveal unresolved package
136
+ * references. Returns the longest resolvable npm subpath for each dependency.
137
+ *
138
+ * For example, 'foo::bar::baz' could resolve to:
139
+ * - 'foo/bar' (package foo, export './bar' bundle)
140
+ * - 'foo' (package foo, default export)
141
+ *
142
+ * @param weslSrc - Record of WESL source files by path
143
+ * @param projectDir - Project directory for resolving package imports
144
+ * @param virtualLibNames - Virtual lib names to exclude (e.g., ['test', 'constants'])
145
+ * @returns Dependency paths in npm format (e.g., 'foo/bar', 'foo')
146
+ */
147
+ declare function parseDependencies(weslSrc: Record<string, string>, projectDir: string, virtualLibNames?: string[]): string[];
148
+ /**
149
+ * Load WeslBundle instances referenced by WESL sources.
150
+ *
151
+ * Parses sources to find external module references, then dynamically imports
152
+ * the corresponding weslBundle.js files.
153
+ *
154
+ * @param weslSrc - Record of WESL source files by path
155
+ * @param projectDir - Project directory for resolving imports
156
+ * @param packageName - Optional current package name
157
+ * @param includeCurrentPackage - Include current package in results (default: false)
158
+ * @param virtualLibNames - Virtual lib names to exclude from resolution
159
+ * @returns Loaded WeslBundle instances
160
+ */
161
+ declare function dependencyBundles(weslSrc: Record<string, string>, projectDir: string, packageName?: string, includeCurrentPackage?: boolean, virtualLibNames?: string[]): Promise<WeslBundle[]>;
162
+ //#endregion
163
+ //#region src/ResolveProjectDir.d.ts
164
+ /**
165
+ * Resolves a project directory by searching upward for package.json or wesl.toml.
166
+ *
167
+ * @param startPath - Optional starting path (file:// URL or filesystem path).
168
+ * If a file URL is provided, uses its directory.
169
+ * If omitted or falsy, defaults to process.cwd().
170
+ * @returns file:// URL string pointing to the project directory
171
+ * (the first ancestor containing package.json or wesl.toml, or the start directory)
172
+ */
173
+ declare function resolveProjectDir(startPath?: string): Promise<string>;
174
+ //#endregion
175
+ //#region src/Version.d.ts
176
+ /** Read package.json from a directory.
177
+ * @param projectDir - file:// URL string to directory containing package.json
178
+ * @returns the parsed package.json contents */
179
+ declare function readPackageJson(projectDir: string): Promise<Record<string, any>>;
180
+ /**
181
+ * @param projectDir - file:// URL string to directory containing package.json
182
+ * @returns the 'version' field from the package.json in the `projectDir`
183
+ */
184
+ declare function versionFromPackageJson(projectDir: string): Promise<string>;
185
+ //#endregion
186
+ export { FileModuleResolver, LoadProjectOptions, ProjectInfo, WeslToml, WeslTomlInfo, defaultWeslToml, dependencyBundles, findWeslToml, getPackageName, loadModules, loadProject, loadWeslToml, parseDependencies, readPackageJson, resolveProjectDir, versionFromPackageJson, zip };
package/dist/index.js ADDED
@@ -0,0 +1,406 @@
1
+ import * as fs$1 from "node:fs";
2
+ import { RecordResolver, WeslParseError, filterMap, findUnboundIdents, moduleToRelativePath, normalizeDebugRoot, npmNameVariations, parseSrcModule, sanitizePackageName } from "wesl";
3
+ import fs from "node:fs/promises";
4
+ import * as path$1 from "node:path";
5
+ import path from "node:path";
6
+ import { glob } from "glob";
7
+ import toml from "toml";
8
+ import { fileURLToPath, pathToFileURL } from "node:url";
9
+ import { resolve } from "import-meta-resolve";
10
+
11
+ //#region src/FileModuleResolver.ts
12
+ /**
13
+ * Loads WESL modules from the filesystem on demand with caching.
14
+ *
15
+ * Resolves module paths like `package::foo::bar` to filesystem paths
16
+ * like `baseDir/foo/bar.wesl` or `baseDir/foo/bar.wgsl`.
17
+ */
18
+ var FileModuleResolver = class {
19
+ /** Cached parsed ASTs to avoid re-parsing the same module */
20
+ astCache = /* @__PURE__ */ new Map();
21
+ /** Root directory containing shader source files */
22
+ baseDir;
23
+ /** Package name that this resolver handles (in addition to generic "package") */
24
+ packageName;
25
+ /** Optional root path for debug file paths (for browser-clickable errors) */
26
+ debugWeslRoot;
27
+ /**
28
+ * @param baseDir - Root directory containing shader source files
29
+ * @param packageName - Package name to resolve (defaults to "package")
30
+ * @param debugWeslRoot - Optional root path for debug file paths. If provided, error messages
31
+ * will show paths relative to this root (e.g., "shaders/foo.wesl") instead of absolute
32
+ * filesystem paths. This is needed for clickable errors in browser dev tools.
33
+ */
34
+ constructor(baseDir, packageName = "package", debugWeslRoot) {
35
+ this.baseDir = baseDir;
36
+ this.packageName = packageName;
37
+ this.debugWeslRoot = debugWeslRoot;
38
+ }
39
+ /**
40
+ * Resolves and parses a module by its import path.
41
+ *
42
+ * Returns cached AST if available, otherwise loads from filesystem,
43
+ * parses, caches, and returns the AST. Returns undefined if module
44
+ * cannot be found.
45
+ *
46
+ * @param modulePath - Module path like "package::foo::bar"
47
+ * @returns Parsed AST or undefined if module not found
48
+ */
49
+ resolveModule(modulePath) {
50
+ const cached = this.astCache.get(modulePath);
51
+ if (cached) return cached;
52
+ const sourceFile = this.tryExtensions(modulePath);
53
+ if (!sourceFile) return void 0;
54
+ const ast = parseSrcModule({
55
+ modulePath,
56
+ debugFilePath: this.debugWeslRoot ? this.modulePathToDebugPath(modulePath) : sourceFile.filePath,
57
+ src: sourceFile.source
58
+ });
59
+ this.astCache.set(modulePath, ast);
60
+ return ast;
61
+ }
62
+ /** Try .wesl first, then .wgsl */
63
+ tryExtensions(modulePath) {
64
+ const basePath = this.moduleToFilePath(modulePath);
65
+ if (!basePath) return void 0;
66
+ for (const ext of [".wesl", ".wgsl"]) {
67
+ const filePath = basePath + ext;
68
+ const source = this.loadSource(filePath);
69
+ if (source) return {
70
+ filePath,
71
+ source
72
+ };
73
+ }
74
+ }
75
+ loadSource(filePath) {
76
+ try {
77
+ return fs$1.readFileSync(filePath, "utf8");
78
+ } catch {
79
+ return;
80
+ }
81
+ }
82
+ /** Convert module path (package::foo::bar) to filesystem path (baseDir/foo/bar) */
83
+ moduleToFilePath(modulePath) {
84
+ const relativePath = moduleToRelativePath(modulePath, this.packageName);
85
+ if (!relativePath) return void 0;
86
+ return `${this.baseDir}/${relativePath}`;
87
+ }
88
+ /** Convert module path to debug path for error messages */
89
+ modulePathToDebugPath(modulePath) {
90
+ const relative = moduleToRelativePath(modulePath, this.packageName) ?? "";
91
+ return normalizeDebugRoot(this.debugWeslRoot) + relative + ".wesl";
92
+ }
93
+ };
94
+
95
+ //#endregion
96
+ //#region src/LoadWeslToml.ts
97
+ /** Default configuration when no wesl.toml is found */
98
+ const defaultWeslToml = {
99
+ edition: "unstable_2025",
100
+ include: ["shaders/**/*.w[eg]sl"],
101
+ root: "shaders",
102
+ dependencies: "auto"
103
+ };
104
+ /**
105
+ * Load and parse a wesl.toml file from the fs.
106
+ * Provide default values for any required WeslToml fields.
107
+ */
108
+ async function loadWeslToml(tomlFile) {
109
+ const tomlString = await fs.readFile(tomlFile, "utf-8");
110
+ const parsed = toml.parse(tomlString);
111
+ return {
112
+ ...defaultWeslToml,
113
+ ...parsed
114
+ };
115
+ }
116
+ /**
117
+ * Find and load the wesl.toml file, or use defaults if not found
118
+ *
119
+ * @param projectDir The directory to search for wesl.toml (typically cwd or project root)
120
+ * @param specifiedToml Optional explicit path to a toml file
121
+ * @returns Information about the loaded TOML configuration
122
+ */
123
+ async function findWeslToml(projectDir, specifiedToml) {
124
+ let tomlFile;
125
+ if (specifiedToml) {
126
+ await fs.access(specifiedToml);
127
+ tomlFile = specifiedToml;
128
+ } else {
129
+ const tomlPath = path.join(projectDir, "wesl.toml");
130
+ tomlFile = await fs.access(tomlPath).then(() => tomlPath).catch(() => {});
131
+ }
132
+ let parsedToml;
133
+ let tomlDir;
134
+ if (tomlFile) {
135
+ parsedToml = await loadWeslToml(tomlFile);
136
+ tomlDir = path.dirname(tomlFile);
137
+ } else {
138
+ parsedToml = defaultWeslToml;
139
+ tomlDir = projectDir;
140
+ }
141
+ const tomlToWeslRoot = path.resolve(tomlDir, parsedToml.root);
142
+ const projectDirAbs = path.resolve(projectDir);
143
+ const resolvedRoot = path.relative(projectDirAbs, tomlToWeslRoot);
144
+ return {
145
+ tomlFile,
146
+ tomlDir,
147
+ resolvedRoot,
148
+ toml: parsedToml
149
+ };
150
+ }
151
+
152
+ //#endregion
153
+ //#region src/LoadModules.ts
154
+ /**
155
+ * Load the wesl/wgsl shader sources.
156
+ *
157
+ * If baseDir or srcGlob are not provided, this function will attempt to read
158
+ * configuration from wesl.toml in the projectDir. If no wesl.toml exists,
159
+ * default values will be used.
160
+ *
161
+ * @param projectDir The project directory (typically cwd or directory containing package.json)
162
+ * @param baseDir Optional base directory for shaders (overrides wesl.toml if provided)
163
+ * @param srcGlob Optional glob pattern for shader files (overrides wesl.toml if provided)
164
+ */
165
+ async function loadModules(projectDir, baseDir, srcGlob) {
166
+ let resolvedBaseDir;
167
+ let resolvedSrcGlob;
168
+ if (!baseDir || !srcGlob) {
169
+ const tomlInfo = await findWeslToml(projectDir);
170
+ resolvedBaseDir = baseDir ?? tomlInfo.resolvedRoot;
171
+ resolvedSrcGlob = srcGlob ?? tomlInfo.toml.include[0];
172
+ } else {
173
+ resolvedBaseDir = baseDir;
174
+ resolvedSrcGlob = srcGlob;
175
+ }
176
+ const shaderFiles = (await glob(`${resolvedSrcGlob}`, {
177
+ cwd: projectDir,
178
+ ignore: "node_modules/**"
179
+ })).map((f) => path.resolve(projectDir, f));
180
+ const promisedSrcs = shaderFiles.map((f) => fs.readFile(f, { encoding: "utf8" }));
181
+ const src = await Promise.all(promisedSrcs);
182
+ if (src.length === 0) throw new Error(`no WGSL/WESL files found in ${resolvedSrcGlob}`);
183
+ const baseDirAbs = path.resolve(projectDir, resolvedBaseDir);
184
+ const moduleEntries = zip(shaderFiles.map((p) => path.relative(baseDirAbs, path.resolve(p))).map((p) => p.replace(/\\/g, "/")), src.map((s) => s.replace(/\r\n/g, "\n")));
185
+ return Object.fromEntries(moduleEntries);
186
+ }
187
+ function zip(as, bs) {
188
+ return as.map((a, i) => [a, bs[i]]);
189
+ }
190
+
191
+ //#endregion
192
+ //#region src/NpmResolver.ts
193
+ /** Find longest resolvable npm subpath from WESL module path segments.
194
+ *
195
+ * A WESL statement containing a WESL module path like 'import foo__bar::baz::elem;' references
196
+ * an npm package, an export within that package, a module within the WeslBundle,
197
+ * and an element within the WESL module.
198
+ * This function returns the npm package and export portion from the module path.
199
+ * The return value is usable to dynamically import the corresponding weslBundle.js file.
200
+ *
201
+ * Translation from a WESL module path to an npm package path involves:
202
+ * - Mapping WESL package names to their npm counterparts (e.g., 'foo__bar' -> '@foo/bar')
203
+ * - Probing to find the longest valid export subpath within the package
204
+ * - package.json allows export subpaths, so 'mypkg::gpu' could be 'mypkg/gpu' or just 'mypkg' in npm
205
+ * - Probing to handle variations in package naming
206
+ * - foo_bar could be foo-bar in npm
207
+ *
208
+ * Note that the resolution is based on package.json.
209
+ * The resolved file itself may not exist yet. (e.g. dist/weslBundle.js may not have been built yet)
210
+ *
211
+ * @param mPath - Module path segments
212
+ * @param importerURL - Base URL for resolution (e.g., 'file:///path/to/project/')
213
+ * @returns Longest resolvable subpath (e.g., 'foo/bar/baz' or 'foo')
214
+ */
215
+ function npmResolveWESL(mPath, importerURL) {
216
+ for (const subPath of exportSubpaths(mPath)) for (const npmPath of npmNameVariations(subPath)) if (tryResolve(npmPath, importerURL)) return npmPath;
217
+ }
218
+ /** Try Node.js module resolution.
219
+ * @return undefined if unresolvable. */
220
+ function tryResolve(path$2, importerURL) {
221
+ try {
222
+ return resolve(path$2, importerURL);
223
+ } catch {
224
+ return;
225
+ }
226
+ }
227
+ /** Yield possible export subpaths from module path, longest first.
228
+ * Drops the last segment (element name) and iterates down. */
229
+ function* exportSubpaths(mPath) {
230
+ const longest = mPath.length - 1;
231
+ for (let i = longest; i >= 0; i--) yield mPath.slice(0, i).join("/");
232
+ }
233
+
234
+ //#endregion
235
+ //#region src/ParseDependencies.ts
236
+ /**
237
+ * Find package dependencies in WESL source files.
238
+ *
239
+ * Parses sources and partially binds identifiers to reveal unresolved package
240
+ * references. Returns the longest resolvable npm subpath for each dependency.
241
+ *
242
+ * For example, 'foo::bar::baz' could resolve to:
243
+ * - 'foo/bar' (package foo, export './bar' bundle)
244
+ * - 'foo' (package foo, default export)
245
+ *
246
+ * @param weslSrc - Record of WESL source files by path
247
+ * @param projectDir - Project directory for resolving package imports
248
+ * @param virtualLibNames - Virtual lib names to exclude (e.g., ['test', 'constants'])
249
+ * @returns Dependency paths in npm format (e.g., 'foo/bar', 'foo')
250
+ */
251
+ function parseDependencies(weslSrc, projectDir, virtualLibNames = []) {
252
+ let resolver;
253
+ try {
254
+ resolver = new RecordResolver(weslSrc);
255
+ } catch (e) {
256
+ if (e.cause instanceof WeslParseError) {
257
+ console.error(e.message, "\n");
258
+ return [];
259
+ }
260
+ throw e;
261
+ }
262
+ const unbound = findUnboundIdents(resolver);
263
+ if (!unbound) return [];
264
+ const excludeRoots = new Set(["constants", ...virtualLibNames]);
265
+ const pkgRefs = unbound.filter((modulePath) => modulePath.length > 1 && !excludeRoots.has(modulePath[0]));
266
+ if (pkgRefs.length === 0) return [];
267
+ const projectURL = projectDirURL(projectDir);
268
+ const deps = filterMap(pkgRefs, (mPath) => npmResolveWESL(mPath, projectURL));
269
+ return [...new Set(deps)];
270
+ }
271
+ /**
272
+ * Load WeslBundle instances referenced by WESL sources.
273
+ *
274
+ * Parses sources to find external module references, then dynamically imports
275
+ * the corresponding weslBundle.js files.
276
+ *
277
+ * @param weslSrc - Record of WESL source files by path
278
+ * @param projectDir - Project directory for resolving imports
279
+ * @param packageName - Optional current package name
280
+ * @param includeCurrentPackage - Include current package in results (default: false)
281
+ * @param virtualLibNames - Virtual lib names to exclude from resolution
282
+ * @returns Loaded WeslBundle instances
283
+ */
284
+ async function dependencyBundles(weslSrc, projectDir, packageName, includeCurrentPackage = false, virtualLibNames = []) {
285
+ const deps = parseDependencies(weslSrc, projectDir, virtualLibNames);
286
+ const filteredDeps = includeCurrentPackage ? deps : otherPackages(deps, packageName);
287
+ const projectURL = projectDirURL(projectDir);
288
+ const bundles = filteredDeps.map(async (dep) => {
289
+ return (await import(resolve(dep, projectURL))).default;
290
+ });
291
+ return await Promise.all(bundles);
292
+ }
293
+ /** Exclude current package from dependency list. */
294
+ function otherPackages(deps, packageName) {
295
+ if (!packageName) return deps;
296
+ return deps.filter((dep) => dep !== packageName && !dep.startsWith(`${packageName}/`));
297
+ }
298
+ /** Normalize project directory to file:// URL with trailing slash. */
299
+ function projectDirURL(projectDir) {
300
+ if (projectDir.startsWith("file://")) return projectDir.endsWith("/") ? projectDir : `${projectDir}/`;
301
+ const fileUrl = pathToFileURL(projectDir).href;
302
+ return fileUrl.endsWith("/") ? fileUrl : `${fileUrl}/`;
303
+ }
304
+
305
+ //#endregion
306
+ //#region src/ResolveProjectDir.ts
307
+ /**
308
+ * Resolves a project directory by searching upward for package.json or wesl.toml.
309
+ *
310
+ * @param startPath - Optional starting path (file:// URL or filesystem path).
311
+ * If a file URL is provided, uses its directory.
312
+ * If omitted or falsy, defaults to process.cwd().
313
+ * @returns file:// URL string pointing to the project directory
314
+ * (the first ancestor containing package.json or wesl.toml, or the start directory)
315
+ */
316
+ async function resolveProjectDir(startPath) {
317
+ let dir;
318
+ if (!startPath) dir = process.cwd();
319
+ else if (startPath.startsWith("file://")) {
320
+ const fsPath = fileURLToPath(startPath);
321
+ dir = await isFile(fsPath) ? path.dirname(fsPath) : fsPath;
322
+ } else dir = await isFile(startPath) ? path.dirname(startPath) : startPath;
323
+ let current = path.resolve(dir);
324
+ while (true) {
325
+ const hasPkgJson = await fileExists(path.join(current, "package.json"));
326
+ const hasWeslToml = await fileExists(path.join(current, "wesl.toml"));
327
+ if (hasPkgJson || hasWeslToml) return pathToFileURL(current).href;
328
+ const parent = path.dirname(current);
329
+ if (parent === current) return pathToFileURL(dir).href;
330
+ current = parent;
331
+ }
332
+ }
333
+ async function fileExists(filePath) {
334
+ try {
335
+ await fs.access(filePath);
336
+ return true;
337
+ } catch {
338
+ return false;
339
+ }
340
+ }
341
+ async function isFile(fsPath) {
342
+ try {
343
+ return (await fs.stat(fsPath)).isFile();
344
+ } catch {
345
+ return false;
346
+ }
347
+ }
348
+
349
+ //#endregion
350
+ //#region src/Version.ts
351
+ /** Read package.json from a directory.
352
+ * @param projectDir - file:// URL string to directory containing package.json
353
+ * @returns the parsed package.json contents */
354
+ async function readPackageJson(projectDir) {
355
+ const baseUrl = projectDir.endsWith("/") ? projectDir : `${projectDir}/`;
356
+ return (await import(new URL("package.json", baseUrl).href, { with: { type: "json" } })).default;
357
+ }
358
+ /**
359
+ * @param projectDir - file:// URL string to directory containing package.json
360
+ * @returns the 'version' field from the package.json in the `projectDir`
361
+ */
362
+ async function versionFromPackageJson(projectDir) {
363
+ return (await readPackageJson(projectDir)).version;
364
+ }
365
+
366
+ //#endregion
367
+ //#region src/LoadProject.ts
368
+ /**
369
+ * Load everything needed to link a shader file.
370
+ *
371
+ * Discovers the project root, loads wesl.toml config, reads all shader modules,
372
+ * and resolves external library dependencies from npm.
373
+ *
374
+ * @returns ProjectInfo with sources, libs, and module paths, or null if not in a project.
375
+ */
376
+ async function loadProject(filePath, opts = {}) {
377
+ const { virtualLibs = [] } = opts;
378
+ try {
379
+ const projectDir = fileURLToPath(await resolveProjectDir(filePath));
380
+ const tomlInfo = await findWeslToml(projectDir);
381
+ const packageName = await getPackageName(projectDir);
382
+ const weslSrc = await loadModules(projectDir);
383
+ const libs = (await dependencyBundles(weslSrc, projectDir, packageName, false, virtualLibs)).filter(Boolean);
384
+ const shaderRootAbs = path$1.resolve(projectDir, tomlInfo.resolvedRoot);
385
+ return {
386
+ weslSrc,
387
+ rootModuleName: path$1.relative(shaderRootAbs, filePath).replace(/\\/g, "/"),
388
+ packageName,
389
+ libs
390
+ };
391
+ } catch {
392
+ return null;
393
+ }
394
+ }
395
+ /** Read package name from package.json, sanitized for WESL identifiers. */
396
+ async function getPackageName(projectDir) {
397
+ try {
398
+ const projectUrl = pathToFileURL(projectDir).href;
399
+ return sanitizePackageName((await readPackageJson(projectUrl)).name);
400
+ } catch {
401
+ return path$1.basename(projectDir);
402
+ }
403
+ }
404
+
405
+ //#endregion
406
+ export { FileModuleResolver, defaultWeslToml, dependencyBundles, findWeslToml, getPackageName, loadModules, loadProject, loadWeslToml, parseDependencies, readPackageJson, resolveProjectDir, versionFromPackageJson, zip };
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "wesl-tooling",
3
- "version": "0.6.21",
3
+ "version": "0.6.22",
4
4
  "type": "module",
5
5
  "files": [
6
+ "dist",
6
7
  "src"
7
8
  ],
8
9
  "repository": "github:wgsl-tooling-wg/wesl-js",
9
10
  "exports": {
10
11
  ".": {
11
- "import": "./src/index.ts"
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
12
14
  }
13
15
  },
14
16
  "dependencies": {
@@ -20,11 +22,13 @@
20
22
  "dependent_package": "x"
21
23
  },
22
24
  "peerDependencies": {
23
- "wesl": "^0.7.8"
25
+ "wesl": "^0.7.9"
24
26
  },
25
27
  "scripts": {
28
+ "build": "tsdown",
26
29
  "test": "cross-env FORCE_COLOR=1 vitest",
27
30
  "test:once": "vitest run",
28
31
  "typecheck": "tsgo"
29
- }
32
+ },
33
+ "main": "dist/index.js"
30
34
  }