wesl-plugin 0.6.69 → 0.6.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -96,10 +96,10 @@ export default {
96
96
 
97
97
  ### Conditions in Import Path
98
98
 
99
- For `?static`, you can specify conditions directly in the import:
99
+ For `?static`, you can specify conditions as query parameters:
100
100
 
101
101
  ```ts
102
- import wgsl from "./app.wesl MOBILE=true DEBUG=false ?static";
102
+ import wgsl from "./app.wesl?MOBILE=true&DEBUG=false&static";
103
103
  ```
104
104
 
105
105
  ## Configuration (wesl.toml)
@@ -124,7 +124,7 @@ import type { PluginExtension } from "wesl-plugin";
124
124
 
125
125
  const myExtension: PluginExtension = {
126
126
  extensionName: "myfeature", // enables ?myfeature imports
127
- emitFn: async (shaderPath, api, conditions) => {
127
+ emitFn: async (shaderPath, api, conditions, options) => {
128
128
  const sources = await api.weslSrc();
129
129
  // Return JavaScript code as a string
130
130
  return `export default ${JSON.stringify(sources)};`;
@@ -31,10 +31,16 @@ interface WeslTomlInfo {
31
31
  }
32
32
  //#endregion
33
33
  //#region src/PluginExtension.d.ts
34
- /** function type required for for emit extensions */
34
+ /** function type required for emit extensions */
35
35
  type ExtensionEmitFn = (/** absolute path to the shader to which the extension is attached */
36
+
36
37
  shaderPath: string, /** support functions available to plugin extensions */
37
- pluginApi: PluginExtensionApi, /** static conditions specified on the js import */conditions?: Record<string, boolean>) => Promise<string>;
38
+
39
+ pluginApi: PluginExtensionApi, /** static conditions specified on the js import */
40
+
41
+ conditions?: Record<string, boolean>, /** plugin-level options from query params (e.g., { include: "all" }) */
42
+
43
+ options?: Record<string, string>) => Promise<string>;
38
44
  /** an extension that runs inside the wesl-js build plugin */
39
45
  interface PluginExtension extends WeslJsPlugin {
40
46
  /** javascript imports with this suffix will trigger the plugin */
@@ -43,6 +49,10 @@ interface PluginExtension extends WeslJsPlugin {
43
49
  * e.g. import myPluginJs from "./foo.wesl?myPlugin"; */
44
50
  emitFn: ExtensionEmitFn;
45
51
  }
52
+ interface ProjectSources {
53
+ weslSrc: Record<string, string>;
54
+ dependencies: string[];
55
+ }
46
56
  /** api supplied to plugin extensions */
47
57
  interface PluginExtensionApi {
48
58
  weslToml: () => Promise<WeslTomlInfo>;
@@ -50,6 +60,12 @@ interface PluginExtensionApi {
50
60
  weslRegistry: () => Promise<BatchModuleResolver>;
51
61
  weslMain: (baseId: string) => Promise<string>;
52
62
  weslDependencies: () => Promise<string[]>;
63
+ /** weslRoot relative to tomlDir, with forward slashes. */
64
+ debugWeslRoot: () => Promise<string>;
65
+ /** Get weslSrc scoped to modules reachable from a root, plus their deps. */
66
+ scopedProject: (rootModuleName: string) => Promise<ProjectSources>;
67
+ /** Fetch project sources, either all or scoped to reachable modules. */
68
+ fetchProject: (rootModuleName: string, options?: Record<string, string>) => Promise<ProjectSources>;
53
69
  }
54
70
  //#endregion
55
- export { PluginExtension as n, PluginExtensionApi as r, ExtensionEmitFn as t };
71
+ export { ProjectSources as i, PluginExtension as n, PluginExtensionApi as r, ExtensionEmitFn as t };
@@ -14,12 +14,9 @@ const linkBuildExtension = {
14
14
  emitFn: emitLinkJs
15
15
  };
16
16
  /** Emit a JavaScript LinkParams structure, ready for linking at runtime. */
17
- async function emitLinkJs(baseId, api) {
18
- const { resolvedRoot, tomlDir } = await api.weslToml();
19
- const weslSrc = await api.weslSrc();
17
+ async function emitLinkJs(baseId, api, _conditions, options) {
20
18
  const rootModuleName = noSuffix(await api.weslMain(baseId));
21
- const debugWeslRoot = path.relative(tomlDir, resolvedRoot).replaceAll(path.sep, "/");
22
- const autoDeps = await api.weslDependencies();
19
+ const [{ weslSrc, dependencies: autoDeps }, debugWeslRoot] = await Promise.all([api.fetchProject(rootModuleName, options), api.debugWeslRoot()]);
23
20
  const sanitizedDeps = autoDeps.map((dep) => dep.replaceAll("/", "_"));
24
21
  const bundleImports = autoDeps.map((p, i) => `import ${sanitizedDeps[i]} from "${p}";`).join("\n");
25
22
  const paramsName = `link${path.basename(rootModuleName).replace(/\W/g, "_")}Config`;
@@ -165,8 +162,8 @@ codes.ERR_INVALID_PACKAGE_CONFIG = createError(
165
162
  * @param {string} [base]
166
163
  * @param {string} [message]
167
164
  */
168
- (path$1, base, message) => {
169
- return `Invalid package config ${path$1}${base ? ` while importing ${base}` : ""}${message ? `. ${message}` : ""}`;
165
+ (path, base, message) => {
166
+ return `Invalid package config ${path}${base ? ` while importing ${base}` : ""}${message ? `. ${message}` : ""}`;
170
167
  },
171
168
  Error
172
169
  );
@@ -196,8 +193,8 @@ codes.ERR_MODULE_NOT_FOUND = createError(
196
193
  * @param {string} base
197
194
  * @param {boolean} [exactUrl]
198
195
  */
199
- (path$1, base, exactUrl = false) => {
200
- return `Cannot find ${exactUrl ? "module" : "package"} '${path$1}' imported from ${base}`;
196
+ (path, base, exactUrl = false) => {
197
+ return `Cannot find ${exactUrl ? "module" : "package"} '${path}' imported from ${base}`;
201
198
  },
202
199
  Error
203
200
  );
@@ -235,8 +232,8 @@ codes.ERR_UNKNOWN_FILE_EXTENSION = createError(
235
232
  * @param {string} extension
236
233
  * @param {string} path
237
234
  */
238
- (extension, path$1) => {
239
- return `Unknown file extension "${extension}" for ${path$1}`;
235
+ (extension, path) => {
236
+ return `Unknown file extension "${extension}" for ${path}`;
240
237
  },
241
238
  TypeError
242
239
  );
@@ -471,8 +468,8 @@ function getPackageScopeConfig(resolved) {
471
468
  * @param {URL} url - The URL to get the package type for.
472
469
  * @returns {PackageType}
473
470
  */
474
- function getPackageType(url$1) {
475
- return getPackageScopeConfig(url$1).type;
471
+ function getPackageType(url) {
472
+ return getPackageScopeConfig(url).type;
476
473
  }
477
474
 
478
475
  //#endregion
@@ -537,8 +534,8 @@ function getDataProtocolModuleFormat(parsed) {
537
534
  * @param {URL} url
538
535
  * @returns {string}
539
536
  */
540
- function extname(url$1) {
541
- const pathname = url$1.pathname;
537
+ function extname(url) {
538
+ const pathname = url.pathname;
542
539
  let index = pathname.length;
543
540
  while (index--) {
544
541
  const code = pathname.codePointAt(index);
@@ -550,22 +547,22 @@ function extname(url$1) {
550
547
  /**
551
548
  * @type {ProtocolHandler}
552
549
  */
553
- function getFileProtocolModuleFormat(url$1, _context, ignoreErrors) {
554
- const value = extname(url$1);
550
+ function getFileProtocolModuleFormat(url, _context, ignoreErrors) {
551
+ const value = extname(url);
555
552
  if (value === ".js") {
556
- const packageType = getPackageType(url$1);
553
+ const packageType = getPackageType(url);
557
554
  if (packageType !== "none") return packageType;
558
555
  return "commonjs";
559
556
  }
560
557
  if (value === "") {
561
- const packageType = getPackageType(url$1);
558
+ const packageType = getPackageType(url);
562
559
  if (packageType === "none" || packageType === "commonjs") return "commonjs";
563
560
  return "module";
564
561
  }
565
- const format$1 = extensionFormatMap[value];
566
- if (format$1) return format$1;
562
+ const format = extensionFormatMap[value];
563
+ if (format) return format;
567
564
  if (ignoreErrors) return;
568
- throw new ERR_UNKNOWN_FILE_EXTENSION(value, fileURLToPath(url$1));
565
+ throw new ERR_UNKNOWN_FILE_EXTENSION(value, fileURLToPath(url));
569
566
  }
570
567
  function getHttpProtocolModuleFormat() {}
571
568
  /**
@@ -573,10 +570,10 @@ function getHttpProtocolModuleFormat() {}
573
570
  * @param {{parentURL: string}} context
574
571
  * @returns {string | null}
575
572
  */
576
- function defaultGetFormatWithoutErrors(url$1, context) {
577
- const protocol = url$1.protocol;
573
+ function defaultGetFormatWithoutErrors(url, context) {
574
+ const protocol = url.protocol;
578
575
  if (!hasOwnProperty.call(protocolHandlers, protocol)) return null;
579
- return protocolHandlers[protocol](url$1, context, true) || null;
576
+ return protocolHandlers[protocol](url, context, true) || null;
580
577
  }
581
578
 
582
579
  //#endregion
@@ -649,10 +646,10 @@ function emitInvalidSegmentDeprecation(target, request, match, packageJsonUrl, i
649
646
  * @param {string} [main]
650
647
  * @returns {void}
651
648
  */
652
- function emitLegacyIndexDeprecation(url$1, packageJsonUrl, base, main) {
649
+ function emitLegacyIndexDeprecation(url, packageJsonUrl, base, main) {
653
650
  if (process.noDeprecation) return;
654
- if (defaultGetFormatWithoutErrors(url$1, { parentURL: base.href }) !== "module") return;
655
- const urlPath = fileURLToPath(url$1.href);
651
+ if (defaultGetFormatWithoutErrors(url, { parentURL: base.href }) !== "module") return;
652
+ const urlPath = fileURLToPath(url.href);
656
653
  const packagePath = fileURLToPath(new URL$1(".", packageJsonUrl));
657
654
  const basePath = fileURLToPath(base);
658
655
  if (!main) process.emitWarning(`No "main" or "exports" field defined in the package.json for ${packagePath} resolving the main entry point "${urlPath.slice(packagePath.length)}", imported from ${basePath}.\nDefault "index" lookups for the main are deprecated for ES modules.`, "DeprecationWarning", "DEP0151");
@@ -662,9 +659,9 @@ function emitLegacyIndexDeprecation(url$1, packageJsonUrl, base, main) {
662
659
  * @param {string} path
663
660
  * @returns {Stats | undefined}
664
661
  */
665
- function tryStatSync(path$1) {
662
+ function tryStatSync(path) {
666
663
  try {
667
- return statSync(path$1);
664
+ return statSync(path);
668
665
  } catch {}
669
666
  }
670
667
  /**
@@ -678,8 +675,8 @@ function tryStatSync(path$1) {
678
675
  * @param {URL} url
679
676
  * @returns {boolean}
680
677
  */
681
- function fileExists(url$1) {
682
- const stats = statSync(url$1, { throwIfNoEntry: false });
678
+ function fileExists(url) {
679
+ const stats = statSync(url, { throwIfNoEntry: false });
683
680
  const isFile = stats ? stats.isFile() : void 0;
684
681
  return isFile === null || isFile === void 0 ? false : isFile;
685
682
  }
@@ -695,7 +692,7 @@ function legacyMainResolve(packageJsonUrl, packageConfig, base) {
695
692
  if (packageConfig.main !== void 0) {
696
693
  guess = new URL$1(packageConfig.main, packageJsonUrl);
697
694
  if (fileExists(guess)) return guess;
698
- const tries$1 = [
695
+ const tries = [
699
696
  `./${packageConfig.main}.js`,
700
697
  `./${packageConfig.main}.json`,
701
698
  `./${packageConfig.main}.node`,
@@ -703,9 +700,9 @@ function legacyMainResolve(packageJsonUrl, packageConfig, base) {
703
700
  `./${packageConfig.main}/index.json`,
704
701
  `./${packageConfig.main}/index.node`
705
702
  ];
706
- let i$1 = -1;
707
- while (++i$1 < tries$1.length) {
708
- guess = new URL$1(tries$1[i$1], packageJsonUrl);
703
+ let i = -1;
704
+ while (++i < tries.length) {
705
+ guess = new URL$1(tries[i], packageJsonUrl);
709
706
  if (fileExists(guess)) break;
710
707
  guess = void 0;
711
708
  }
@@ -826,12 +823,12 @@ function resolvePackageTargetString(target, subpath, match, packageJsonUrl, base
826
823
  if (subpath !== "" && !pattern && target[target.length - 1] !== "/") throw invalidPackageTarget(match, target, packageJsonUrl, internal, base);
827
824
  if (!target.startsWith("./")) {
828
825
  if (internal && !target.startsWith("../") && !target.startsWith("/")) {
829
- let isURL$1 = false;
826
+ let isURL = false;
830
827
  try {
831
828
  new URL$1(target);
832
- isURL$1 = true;
829
+ isURL = true;
833
830
  } catch {}
834
- if (!isURL$1) return packageResolve(pattern ? RegExpPrototypeSymbolReplace.call(patternRegEx, target, () => subpath) : target + subpath, packageJsonUrl, conditions);
831
+ if (!isURL) return packageResolve(pattern ? RegExpPrototypeSymbolReplace.call(patternRegEx, target, () => subpath) : target + subpath, packageJsonUrl, conditions);
835
832
  }
836
833
  throw invalidPackageTarget(match, target, packageJsonUrl, internal, base);
837
834
  }
@@ -1096,8 +1093,8 @@ function packageResolve(specifier, base, conditions) {
1096
1093
  const packageConfig = getPackageScopeConfig(base);
1097
1094
  /* c8 ignore next 16 */
1098
1095
  if (packageConfig.exists) {
1099
- const packageJsonUrl$1 = pathToFileURL(packageConfig.pjsonPath);
1100
- if (packageConfig.name === packageName && packageConfig.exports !== void 0 && packageConfig.exports !== null) return packageExportsResolve(packageJsonUrl$1, packageSubpath, packageConfig, base, conditions);
1096
+ const packageJsonUrl = pathToFileURL(packageConfig.pjsonPath);
1097
+ if (packageConfig.name === packageName && packageConfig.exports !== void 0 && packageConfig.exports !== null) return packageExportsResolve(packageJsonUrl, packageSubpath, packageConfig, base, conditions);
1101
1098
  }
1102
1099
  let packageJsonUrl = new URL$1("./node_modules/" + packageName + "/package.json", base);
1103
1100
  let packageJsonPath = fileURLToPath(packageJsonUrl);
@@ -1111,12 +1108,12 @@ function packageResolve(specifier, base, conditions) {
1111
1108
  packageJsonPath = fileURLToPath(packageJsonUrl);
1112
1109
  continue;
1113
1110
  }
1114
- const packageConfig$1 = read(packageJsonPath, {
1111
+ const packageConfig = read(packageJsonPath, {
1115
1112
  base,
1116
1113
  specifier
1117
1114
  });
1118
- if (packageConfig$1.exports !== void 0 && packageConfig$1.exports !== null) return packageExportsResolve(packageJsonUrl, packageSubpath, packageConfig$1, base, conditions);
1119
- if (packageSubpath === ".") return legacyMainResolve(packageJsonUrl, packageConfig$1, base);
1115
+ if (packageConfig.exports !== void 0 && packageConfig.exports !== null) return packageExportsResolve(packageJsonUrl, packageSubpath, packageConfig, base, conditions);
1116
+ if (packageSubpath === ".") return legacyMainResolve(packageJsonUrl, packageConfig, base);
1120
1117
  return new URL$1(packageSubpath, packageJsonUrl);
1121
1118
  } while (packageJsonPath.length !== lastPath.length);
1122
1119
  throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), false);
@@ -1261,10 +1258,10 @@ function defaultResolve(specifier, context = {}) {
1261
1258
  if (protocol === "node:") return { url: specifier };
1262
1259
  if (parsed && parsed.protocol === "node:") return { url: specifier };
1263
1260
  const conditions = getConditionsSet(context.conditions);
1264
- const url$1 = moduleResolve(specifier, new URL$1(parentURL), conditions, false);
1261
+ const url = moduleResolve(specifier, new URL$1(parentURL), conditions, false);
1265
1262
  return {
1266
- url: url$1.href,
1267
- format: defaultGetFormatWithoutErrors(url$1, { parentURL })
1263
+ url: url.href,
1264
+ format: defaultGetFormatWithoutErrors(url, { parentURL })
1268
1265
  };
1269
1266
  }
1270
1267
 
@@ -1301,34 +1298,28 @@ function resolve(specifier, parent) {
1301
1298
 
1302
1299
  //#endregion
1303
1300
  //#region src/extensions/StaticExtension.ts
1304
- /**
1305
- * a wesl-js ?static build extension that statically links from the root file
1306
- * and emits a JavaScript file containing the linked wgsl string.
1307
- *
1308
- * use it like this:
1309
- * import wgsl from "./shaders/app.wesl?static";
1310
- *
1311
- * or with conditions, like this:
1312
- * import wgsl from "../shaders/foo/app.wesl MOBILE=true FUN SAFE=false ?static";
1313
- */
1301
+ /** Build extension for ?static imports: links WESL at build time, emits WGSL string. */
1314
1302
  const staticBuildExtension = {
1315
1303
  extensionName: "static",
1316
1304
  emitFn: emitStaticJs
1317
1305
  };
1318
- /** Emit a JavaScript file containing the wgsl string */
1319
- async function emitStaticJs(baseId, api, conditions) {
1306
+ /** Emit a JS module exporting the statically linked WGSL string. */
1307
+ async function emitStaticJs(baseId, api, conditions, _options) {
1320
1308
  const { resolvedRoot, tomlDir } = await api.weslToml();
1321
1309
  const parentModule = url.pathToFileURL(path.join(tomlDir, "wesl.toml")).toString();
1322
- const futureLibs = (await api.weslDependencies()).map((d) => resolve(d, parentModule)).map((f) => import(f));
1323
- const libs = (await Promise.all(futureLibs)).map((m) => m.default);
1324
- return `
1325
- export const wgsl = \`${(await link({
1326
- weslSrc: await api.weslSrc(),
1327
- rootModuleName: noSuffix(await api.weslMain(baseId)),
1310
+ const rootModuleName = noSuffix(await api.weslMain(baseId));
1311
+ const [weslSrc, dependencies] = await Promise.all([api.weslSrc(), api.weslDependencies()]);
1312
+ const libFileUrls = dependencies.map((d) => resolve(d, parentModule));
1313
+ const libs = (await Promise.all(libFileUrls.map((f) => import(f)))).map((m) => m.default);
1314
+ const { dest: wgsl } = await link({
1315
+ weslSrc,
1316
+ rootModuleName,
1328
1317
  debugWeslRoot: path.relative(tomlDir, resolvedRoot).replaceAll(path.sep, "/"),
1329
1318
  libs,
1330
1319
  conditions
1331
- })).dest}\`;
1320
+ });
1321
+ return `
1322
+ export const wgsl = \`${wgsl}\`;
1332
1323
  export default wgsl;
1333
1324
  `;
1334
1325
  }