wesl-packager 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.
Files changed (2) hide show
  1. package/bin/wesl-packager +104 -56
  2. package/package.json +2 -2
package/bin/wesl-packager CHANGED
@@ -2,19 +2,19 @@
2
2
  import { builtinModules } from "node:module";
3
3
  import process$1, { exit } from "node:process";
4
4
  import { hideBin } from "yargs/helpers";
5
- import fs, { lstat, mkdir, readdir, readlink, realpath } from "node:fs/promises";
5
+ import * as actualFS from "node:fs";
6
+ import fs, { realpathSync, statSync } from "node:fs";
7
+ import { RecordResolver, WeslParseError, bindIdentsRecursive, filterMap, findValidRootDecls, minimalMangle, noSuffix } from "wesl";
8
+ import fs$1, { lstat, mkdir, readdir, readlink, realpath } from "node:fs/promises";
6
9
  import path, { posix, win32 } from "node:path";
7
10
  import { URL as URL$1, fileURLToPath, pathToFileURL } from "node:url";
8
- import { lstatSync, readdir as readdir$1, readdirSync, readlinkSync, realpathSync } from "fs";
9
- import * as actualFS from "node:fs";
10
- import fs$1, { realpathSync as realpathSync$1, statSync } from "node:fs";
11
+ import { lstatSync, readdir as readdir$1, readdirSync, readlinkSync, realpathSync as realpathSync$1 } from "fs";
11
12
  import { EventEmitter } from "node:events";
12
13
  import Stream from "node:stream";
13
14
  import { StringDecoder } from "node:string_decoder";
14
15
  import assert from "node:assert";
15
16
  import v8 from "node:v8";
16
17
  import { format, inspect } from "node:util";
17
- import { WeslParseError, filterMap, findUnboundIdents, noSuffix, parseIntoRegistry, parsedRegistry } from "wesl";
18
18
  import yargs from "yargs";
19
19
  import { Biome, Distribution } from "@biomejs/js-api";
20
20
 
@@ -43,6 +43,41 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
43
43
  enumerable: true
44
44
  }) : target, mod));
45
45
 
46
+ //#endregion
47
+ //#region ../wesl-tooling/src/FindUnboundIdents.ts
48
+ /**
49
+ * Find unbound package references in library sources.
50
+ *
51
+ * Binds local references without following cross-package imports, revealing
52
+ * which external packages are referenced but not resolved.
53
+ *
54
+ * @param resolver - Module resolver that supports batch operations
55
+ * @returns Array of unbound module paths, each as an array of path segments
56
+ * (e.g., [['foo', 'bar', 'baz'], ['other', 'pkg']])
57
+ */
58
+ function findUnboundIdents(resolver) {
59
+ const bindContext = {
60
+ resolver,
61
+ conditions: {},
62
+ knownDecls: /* @__PURE__ */ new Set(),
63
+ foundScopes: /* @__PURE__ */ new Set(),
64
+ globalNames: /* @__PURE__ */ new Set(),
65
+ globalStatements: /* @__PURE__ */ new Map(),
66
+ mangler: minimalMangle,
67
+ unbound: [],
68
+ dontFollowDecls: true
69
+ };
70
+ for (const [_modulePath, ast] of resolver.allModules()) {
71
+ const declEntries = findValidRootDecls(ast.rootScope, {}).map((d) => [d.originalName, d]);
72
+ const liveDecls = {
73
+ decls: new Map(declEntries),
74
+ parent: null
75
+ };
76
+ bindIdentsRecursive(ast.rootScope, bindContext, liveDecls, true);
77
+ }
78
+ return bindContext.unbound;
79
+ }
80
+
46
81
  //#endregion
47
82
  //#region ../../node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/dist/esm/index.js
48
83
  const balanced = (a, b, str) => {
@@ -3327,7 +3362,7 @@ while (this[FLUSHCHUNK](this[BUFFERSHIFT]()) && this[BUFFER].length);
3327
3362
 
3328
3363
  //#endregion
3329
3364
  //#region ../../node_modules/.pnpm/path-scurry@2.0.0/node_modules/path-scurry/dist/esm/index.js
3330
- const realpathSync$2 = realpathSync.native;
3365
+ const realpathSync$2 = realpathSync$1.native;
3331
3366
  const defaultFS = {
3332
3367
  lstatSync,
3333
3368
  readdir: readdir$1,
@@ -9632,16 +9667,17 @@ var require_toml = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/toml@3
9632
9667
  var import_toml = /* @__PURE__ */ __toESM(require_toml(), 1);
9633
9668
  /** Default configuration when no wesl.toml is found */
9634
9669
  const defaultWeslToml = {
9635
- weslFiles: ["shaders/**/*.w[eg]sl"],
9636
- weslRoot: "shaders",
9637
- dependencies: ["auto"]
9670
+ edition: "unstable_2025",
9671
+ include: ["shaders/**/*.w[eg]sl"],
9672
+ root: "shaders",
9673
+ dependencies: "auto"
9638
9674
  };
9639
9675
  /**
9640
9676
  * Load and parse a wesl.toml file from the fs.
9641
9677
  * Provide default values for any required WeslToml fields.
9642
9678
  */
9643
9679
  async function loadWeslToml(tomlFile) {
9644
- const tomlString = await fs.readFile(tomlFile, "utf-8");
9680
+ const tomlString = await fs$1.readFile(tomlFile, "utf-8");
9645
9681
  const parsed = import_toml.default.parse(tomlString);
9646
9682
  return {
9647
9683
  ...defaultWeslToml,
@@ -9658,11 +9694,11 @@ async function loadWeslToml(tomlFile) {
9658
9694
  async function findWeslToml(projectDir, specifiedToml) {
9659
9695
  let tomlFile;
9660
9696
  if (specifiedToml) {
9661
- await fs.access(specifiedToml);
9697
+ await fs$1.access(specifiedToml);
9662
9698
  tomlFile = specifiedToml;
9663
9699
  } else {
9664
9700
  const tomlPath = path.join(projectDir, "wesl.toml");
9665
- tomlFile = await fs.access(tomlPath).then(() => tomlPath).catch(() => {});
9701
+ tomlFile = await fs$1.access(tomlPath).then(() => tomlPath).catch(() => {});
9666
9702
  }
9667
9703
  let parsedToml;
9668
9704
  let tomlDir;
@@ -9673,13 +9709,13 @@ async function findWeslToml(projectDir, specifiedToml) {
9673
9709
  parsedToml = defaultWeslToml;
9674
9710
  tomlDir = projectDir;
9675
9711
  }
9676
- const tomlToWeslRoot = path.resolve(tomlDir, parsedToml.weslRoot);
9712
+ const tomlToWeslRoot = path.resolve(tomlDir, parsedToml.root);
9677
9713
  const projectDirAbs = path.resolve(projectDir);
9678
- const resolvedWeslRoot = path.relative(projectDirAbs, tomlToWeslRoot);
9714
+ const resolvedRoot = path.relative(projectDirAbs, tomlToWeslRoot);
9679
9715
  return {
9680
9716
  tomlFile,
9681
9717
  tomlDir,
9682
- resolvedWeslRoot,
9718
+ resolvedRoot,
9683
9719
  toml: parsedToml
9684
9720
  };
9685
9721
  }
@@ -9702,8 +9738,8 @@ async function loadModules(projectDir, baseDir, srcGlob) {
9702
9738
  let resolvedSrcGlob;
9703
9739
  if (!baseDir || !srcGlob) {
9704
9740
  const tomlInfo = await findWeslToml(projectDir);
9705
- resolvedBaseDir = baseDir ?? tomlInfo.resolvedWeslRoot;
9706
- resolvedSrcGlob = srcGlob ?? tomlInfo.toml.weslFiles[0];
9741
+ resolvedBaseDir = baseDir ?? tomlInfo.resolvedRoot;
9742
+ resolvedSrcGlob = srcGlob ?? tomlInfo.toml.include[0];
9707
9743
  } else {
9708
9744
  resolvedBaseDir = baseDir;
9709
9745
  resolvedSrcGlob = srcGlob;
@@ -9712,7 +9748,7 @@ async function loadModules(projectDir, baseDir, srcGlob) {
9712
9748
  cwd: projectDir,
9713
9749
  ignore: "node_modules/**"
9714
9750
  })).map((f) => path.resolve(projectDir, f));
9715
- const promisedSrcs = shaderFiles.map((f) => fs.readFile(f, { encoding: "utf8" }));
9751
+ const promisedSrcs = shaderFiles.map((f) => fs$1.readFile(f, { encoding: "utf8" }));
9716
9752
  const src = await Promise.all(promisedSrcs);
9717
9753
  if (src.length === 0) throw new Error(`no WGSL/WESL files found in ${resolvedSrcGlob}`);
9718
9754
  const baseDirAbs = path.resolve(projectDir, resolvedBaseDir);
@@ -10064,7 +10100,7 @@ function read(jsonPath, { base, specifier }) {
10064
10100
  /** @type {string | undefined} */
10065
10101
  let string;
10066
10102
  try {
10067
- string = fs$1.readFileSync(path.toNamespacedPath(jsonPath), "utf8");
10103
+ string = fs.readFileSync(path.toNamespacedPath(jsonPath), "utf8");
10068
10104
  } catch (error) {
10069
10105
  const exception = error;
10070
10106
  if (exception.code !== "ENOENT") throw exception;
@@ -10410,7 +10446,7 @@ function finalizeResolution(resolved, base, preserveSymlinks) {
10410
10446
  throw error;
10411
10447
  }
10412
10448
  if (!preserveSymlinks) {
10413
- const real = realpathSync$1(filePath);
10449
+ const real = realpathSync(filePath);
10414
10450
  const { search, hash } = resolved;
10415
10451
  resolved = pathToFileURL(real + (filePath.endsWith(path.sep) ? "/" : ""));
10416
10452
  resolved.search = search;
@@ -10948,48 +10984,48 @@ function resolve(specifier, parent) {
10948
10984
  //#endregion
10949
10985
  //#region ../wesl-tooling/src/ParseDependencies.ts
10950
10986
  /**
10951
- * Find the wesl package dependencies in a set of WESL files
10952
- * (for packaging WESL files into a library)
10987
+ * Find package dependencies in WESL source files.
10988
+ *
10989
+ * Parses sources and partially binds identifiers to reveal unresolved package
10990
+ * references. Returns the longest resolvable npm subpath for each dependency.
10953
10991
  *
10954
- * Parse the WESL files and partially bind the identifiers,
10955
- * returning any identifiers that are not succesfully bound.
10956
- * Those identifiers are the package dependencies.
10992
+ * For example, 'foo::bar::baz' could resolve to:
10993
+ * - 'foo/bar' (package foo, export './bar' bundle)
10994
+ * - 'foo' (package foo, default export)
10957
10995
  *
10958
- * The dependency might be a default export bundle or
10959
- * a named export bundle. e.g. for 'foo::bar::baz', it could be
10960
- * . package foo, export '.' bundle, module bar
10961
- * . package foo, export './bar' bundle, element baz
10962
- * . package foo, export './bar/baz' bundle, module lib.wesl, element baz
10963
- * To distinguish these, we node resolve the longest path we can.
10996
+ * @param weslSrc - Record of WESL source files by path
10997
+ * @param projectDir - Project directory for resolving package imports
10998
+ * @returns Dependency paths in npm format (e.g., 'foo/bar', 'foo')
10964
10999
  */
10965
11000
  function parseDependencies(weslSrc, projectDir) {
10966
- const registry = parsedRegistry();
11001
+ let resolver;
10967
11002
  try {
10968
- parseIntoRegistry(weslSrc, registry);
11003
+ resolver = new RecordResolver(weslSrc);
10969
11004
  } catch (e) {
10970
- if (e.cause instanceof WeslParseError) console.error(e.message, "\n");
10971
- else throw e;
11005
+ if (e.cause instanceof WeslParseError) {
11006
+ console.error(e.message, "\n");
11007
+ return [];
11008
+ }
11009
+ throw e;
10972
11010
  }
10973
- const unbound = findUnboundIdents(registry);
11011
+ const unbound = findUnboundIdents(resolver);
10974
11012
  if (!unbound) return [];
10975
11013
  const pkgRefs = unbound.filter((modulePath) => modulePath.length > 1 && modulePath[0] !== "constants");
10976
11014
  if (pkgRefs.length === 0) return [];
10977
- const projectURL = pathToFileURL(path.resolve(path.join(projectDir, "foo"))).href;
11015
+ const projectURL = projectDirURL(projectDir);
10978
11016
  const deps = filterMap(pkgRefs, (mPath) => unboundToDependency(mPath, projectURL));
10979
11017
  return [...new Set(deps)];
10980
11018
  }
10981
- /**
10982
- * Find the longest resolvable npm subpath from a module path.
11019
+ /** Find longest resolvable npm subpath from module path segments.
10983
11020
  *
10984
- * @param mPath module path, e.g. ['foo', 'bar', 'baz', 'elem']
10985
- * @param importerURL URL of the importer, e.g. 'file:///path/to/project/foo/bar/baz.wesl' (doesn't need to be a real file)
10986
- * @returns longest resolvable subpath of mPath, e.g. 'foo/bar/baz' or 'foo/bar'
11021
+ * @param mPath - Module path segments (e.g., ['foo', 'bar', 'baz', 'elem'])
11022
+ * @param importerURL - Base URL for resolution (e.g., 'file:///path/to/project/')
11023
+ * @returns Longest resolvable subpath (e.g., 'foo/bar/baz' or 'foo')
10987
11024
  */
10988
11025
  function unboundToDependency(mPath, importerURL) {
10989
11026
  return [...exportSubpaths(mPath)].find((subPath) => tryResolve(subPath, importerURL));
10990
11027
  }
10991
- /** Try to resolve a path using node's resolve algorithm.
10992
- * @return the resolved path */
11028
+ /** Try Node.js module resolution; returns undefined if unresolvable. */
10993
11029
  function tryResolve(path$2, importerURL) {
10994
11030
  try {
10995
11031
  return resolve(path$2, importerURL);
@@ -10997,23 +11033,34 @@ function tryResolve(path$2, importerURL) {
10997
11033
  return;
10998
11034
  }
10999
11035
  }
11000
- /**
11001
- * Yield possible export entry subpaths from module path
11002
- * longest subpath first.
11003
- */
11036
+ /** Yield possible export subpaths from module path, longest first.
11037
+ * Drops the last segment (element name) and iterates down. */
11004
11038
  function* exportSubpaths(mPath) {
11005
11039
  const longest = mPath.length - 1;
11006
11040
  for (let i = longest; i >= 0; i--) yield mPath.slice(0, i).join("/");
11007
11041
  }
11042
+ /** Normalize project directory to file:// URL with trailing slash. */
11043
+ function projectDirURL(projectDir) {
11044
+ if (projectDir.startsWith("file://")) return projectDir.endsWith("/") ? projectDir : `${projectDir}/`;
11045
+ const fileUrl = pathToFileURL(projectDir).href;
11046
+ return fileUrl.endsWith("/") ? fileUrl : `${fileUrl}/`;
11047
+ }
11008
11048
 
11009
11049
  //#endregion
11010
11050
  //#region ../wesl-tooling/src/Version.ts
11051
+ /** Read package.json from a directory.
11052
+ * @param projectDir - file:// URL string to directory containing package.json
11053
+ * @returns the parsed package.json contents */
11054
+ async function readPackageJson(projectDir) {
11055
+ const baseUrl = projectDir.endsWith("/") ? projectDir : `${projectDir}/`;
11056
+ return (await import(new URL("package.json", baseUrl).href, { with: { type: "json" } })).default;
11057
+ }
11011
11058
  /**
11012
- * @param projectDir - e.g., file:// URL of the directory containing package.json
11059
+ * @param projectDir - file:// URL string to directory containing package.json
11013
11060
  * @returns the 'version' field from the package.json in the `projectDir`
11014
11061
  */
11015
11062
  async function versionFromPackageJson(projectDir) {
11016
- return (await import(new URL("./package.json", projectDir).href, { with: { type: "json" } })).default.version;
11063
+ return (await readPackageJson(projectDir)).version;
11017
11064
  }
11018
11065
 
11019
11066
  //#endregion
@@ -11044,7 +11091,7 @@ async function packageWgsl(args) {
11044
11091
  /** add an 'exports' entry to package.json for the wesl bundles */
11045
11092
  async function updatePackageJson(projectDir, outDir, multiBundle) {
11046
11093
  const pkgJsonPath = path.join(projectDir, "package.json");
11047
- const pkgJsonString = await fs.readFile(pkgJsonPath, { encoding: "utf8" });
11094
+ const pkgJsonString = await fs$1.readFile(pkgJsonPath, { encoding: "utf8" });
11048
11095
  const pkgJson = JSON.parse(pkgJsonString);
11049
11096
  const exports = {};
11050
11097
  const distDir = path.relative(projectDir, outDir);
@@ -11058,7 +11105,7 @@ async function updatePackageJson(projectDir, outDir, multiBundle) {
11058
11105
  };
11059
11106
  const newPkgJson = insertExports(pkgJson, exports);
11060
11107
  const jsonString = JSON.stringify(newPkgJson, null, 2).concat("\n");
11061
- await fs.writeFile(pkgJsonPath, jsonString);
11108
+ await fs$1.writeFile(pkgJsonPath, jsonString);
11062
11109
  }
11063
11110
  /** insert the exports field into the package.json */
11064
11111
  function insertExports(pkgJson, exports) {
@@ -11109,7 +11156,7 @@ async function writeJsBundle(weslBundle, dependencies, outDir) {
11109
11156
  `;
11110
11157
  const outPath = path.join(outDir, "weslBundle.js");
11111
11158
  const formatted = biome.formatContent(biomeKey, outString, { filePath: "b.js" });
11112
- await fs.writeFile(outPath, formatted.content);
11159
+ await fs$1.writeFile(outPath, formatted.content);
11113
11160
  }
11114
11161
  /** Write weslBundle.d.ts containing the type definitions for a WeslBundle */
11115
11162
  async function writeTypeScriptDts(outDir) {
@@ -11119,7 +11166,7 @@ async function writeTypeScriptDts(outDir) {
11119
11166
  `;
11120
11167
  const formatted = biome.formatContent(biomeKey, declText, { filePath: "t.d.ts" });
11121
11168
  const outPath = path.join(outDir, "weslBundle.d.ts");
11122
- await fs.writeFile(outPath, formatted.content);
11169
+ await fs$1.writeFile(outPath, formatted.content);
11123
11170
  }
11124
11171
  /** @return the bundle plus dependencies as a JavaScript string */
11125
11172
  function bundleToJsString(bundle, dependencies) {
@@ -11139,7 +11186,7 @@ function bundleToJsString(bundle, dependencies) {
11139
11186
  /** parse and extract fields from package.json that we care about
11140
11187
  * (the name of the package) */
11141
11188
  async function loadPackageFields(pkgJsonPath) {
11142
- const pkgJsonString = await fs.readFile(pkgJsonPath, { encoding: "utf8" });
11189
+ const pkgJsonString = await fs$1.readFile(pkgJsonPath, { encoding: "utf8" });
11143
11190
  const { name } = JSON.parse(pkgJsonString);
11144
11191
  verifyField("name", name);
11145
11192
  function verifyField(field, value) {
@@ -11174,7 +11221,8 @@ async function packagerCli(rawArgs$1) {
11174
11221
  await packageWgsl(cliArgs);
11175
11222
  }
11176
11223
  async function parseArgs(args) {
11177
- const appVersion = await versionFromPackageJson(new URL("..", import.meta.url));
11224
+ const projectDir = new URL("..", import.meta.url).href;
11225
+ const appVersion = await versionFromPackageJson(projectDir);
11178
11226
  return yargs(args).command("$0", "create an npm package from WGSL/WESL files").version(appVersion).option("src", {
11179
11227
  type: "string",
11180
11228
  describe: "WGSL/WESL files to bundle in the package (glob syntax, defaults to wesl.toml or shaders/**/*.w[eg]sl)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wesl-packager",
3
- "version": "0.6.15",
3
+ "version": "0.6.17",
4
4
  "type": "module",
5
5
  "bin": "bin/wesl-packager",
6
6
  "files": [
@@ -10,7 +10,7 @@
10
10
  "@biomejs/js-api": "^1.0.0",
11
11
  "@biomejs/wasm-nodejs": "^2.0.6",
12
12
  "yargs": "^18.0.0",
13
- "wesl": "0.6.15"
13
+ "wesl": "0.6.17"
14
14
  },
15
15
  "devDependencies": {
16
16
  "dependent_package": "x",