wesl-packager 0.7.21 → 0.7.23
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/dist/main.mjs
CHANGED
|
@@ -8,7 +8,6 @@ import path from "node:path";
|
|
|
8
8
|
import { Biome, Distribution } from "@biomejs/js-api";
|
|
9
9
|
import { noSuffix, sanitizePackageName } from "wesl";
|
|
10
10
|
import { weslBundleDeclUrl } from "wesl/bundle-decl";
|
|
11
|
-
|
|
12
11
|
//#region src/PackageWesl.ts
|
|
13
12
|
const weslBundleDecl = await readFile(new URL(weslBundleDeclUrl), "utf8");
|
|
14
13
|
const { biome, projectKey: biomeKey } = await setupBiome();
|
|
@@ -99,9 +98,7 @@ async function writeJsBundle(weslBundle, dependencies, outDir) {
|
|
|
99
98
|
|
|
100
99
|
export default weslBundle;
|
|
101
100
|
`;
|
|
102
|
-
|
|
103
|
-
const formatted = biome.formatContent(biomeKey, outString, { filePath: "b.js" });
|
|
104
|
-
await fs.writeFile(outPath, formatted.content);
|
|
101
|
+
await safeWrite(path.join(outDir, "weslBundle.js"), biome.formatContent(biomeKey, outString, { filePath: "b.js" }).content);
|
|
105
102
|
}
|
|
106
103
|
/** Write weslBundle.d.ts containing the type definitions for a WeslBundle */
|
|
107
104
|
async function writeTypeScriptDts(outDir) {
|
|
@@ -110,8 +107,18 @@ async function writeTypeScriptDts(outDir) {
|
|
|
110
107
|
export default weslBundle;
|
|
111
108
|
`;
|
|
112
109
|
const formatted = biome.formatContent(biomeKey, declText, { filePath: "t.d.ts" });
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
await safeWrite(path.join(outDir, "weslBundle.d.ts"), formatted.content);
|
|
111
|
+
}
|
|
112
|
+
/** Write without truncating the file (atomic via temp+rename).
|
|
113
|
+
* Skips the write entirely if content is unchanged.
|
|
114
|
+
* Avoids a potential race with parallel tsc readers during `--parallel` builds. */
|
|
115
|
+
async function safeWrite(filePath, content) {
|
|
116
|
+
try {
|
|
117
|
+
if (await fs.readFile(filePath, "utf8") === content) return;
|
|
118
|
+
} catch {}
|
|
119
|
+
const tmpPath = filePath + ".tmp";
|
|
120
|
+
await fs.writeFile(tmpPath, content);
|
|
121
|
+
await fs.rename(tmpPath, filePath);
|
|
115
122
|
}
|
|
116
123
|
/** @return the bundle plus dependencies as a JavaScript string */
|
|
117
124
|
function bundleToJsString(bundle, dependencies) {
|
|
@@ -161,7 +168,6 @@ async function setupBiome() {
|
|
|
161
168
|
projectKey
|
|
162
169
|
};
|
|
163
170
|
}
|
|
164
|
-
|
|
165
171
|
//#endregion
|
|
166
172
|
//#region src/PackagerCli.ts
|
|
167
173
|
let cliArgs;
|
|
@@ -205,7 +211,6 @@ async function parseArgs(args) {
|
|
|
205
211
|
describe: "where to put bundled output files (relative to projectDir)"
|
|
206
212
|
}).help().parse();
|
|
207
213
|
}
|
|
208
|
-
|
|
209
214
|
//#endregion
|
|
210
215
|
//#region src/main.ts
|
|
211
216
|
const rawArgs = hideBin(process.argv);
|
|
@@ -218,7 +223,7 @@ packagerCli(rawArgs).catch((e) => {
|
|
|
218
223
|
console.error(e);
|
|
219
224
|
exit(1);
|
|
220
225
|
});
|
|
221
|
-
|
|
222
226
|
//#endregion
|
|
223
|
-
export {
|
|
227
|
+
export {};
|
|
228
|
+
|
|
224
229
|
//# sourceMappingURL=main.mjs.map
|
package/dist/main.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.mjs","names":[],"sources":["../src/PackageWesl.ts","../src/PackagerCli.ts","../src/main.ts"],"sourcesContent":["import fs, { mkdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { Biome, Distribution } from \"@biomejs/js-api\";\nimport { noSuffix, sanitizePackageName, type WeslBundle } from \"wesl\";\nimport { weslBundleDeclUrl } from \"wesl/bundle-decl\";\nimport { loadModules, parseDependencies, zip } from \"wesl-tooling\";\nimport type { CliArgs } from \"./PackagerCli.ts\";\n\nconst weslBundleDecl = await readFile(new URL(weslBundleDeclUrl), \"utf8\");\n\nconst { biome, projectKey: biomeKey } = await setupBiome();\n\n/** write weslBundle .js and .d.ts files for this shader */\nexport async function packageWgsl(args: CliArgs): Promise<void> {\n const { projectDir, outDir, multiBundle, baseDir, src } = args;\n const modules = await loadModules(projectDir, baseDir, src);\n const pkgJsonPath = path.join(projectDir, \"package.json\");\n const { name } = await loadPackageFields(pkgJsonPath);\n const edition = \"unstable_2025_1\";\n\n if (args.multiBundle) {\n await writeMultiBundle(modules, name, edition, projectDir, outDir);\n } else {\n const allDeps = parseDependencies(modules, projectDir);\n const sanitized = sanitizePackageName(name);\n const deps = filterSelfDeps(allDeps, sanitized);\n const bundle: WeslBundle = { name: sanitized, edition, modules };\n await writeJsBundle(bundle, deps, outDir);\n }\n await writeTypeScriptDts(outDir);\n if (args.updatePackageJson) {\n await updatePackageJson(projectDir, outDir, multiBundle);\n }\n}\n\n/** add an 'exports' entry to package.json for the wesl bundles */\nasync function updatePackageJson(\n projectDir: string,\n outDir: string,\n multiBundle: boolean,\n): Promise<void> {\n const pkgJsonPath = path.join(projectDir, \"package.json\");\n const pkgJsonString = await fs.readFile(pkgJsonPath, { encoding: \"utf8\" });\n const pkgJson = JSON.parse(pkgJsonString);\n const exports: Record<string, any> = {};\n\n const distDir = path.relative(projectDir, outDir);\n if (multiBundle) {\n exports[\"./*\"] = {\n types: `./${distDir}/weslBundle.d.ts`,\n import: `./${distDir}/*/weslBundle.js`,\n };\n } else {\n exports[\".\"] = {\n types: `./${distDir}/weslBundle.d.ts`,\n import: `./${distDir}/weslBundle.js`,\n };\n }\n\n const newPkgJson = insertExports(pkgJson, exports);\n const jsonString = JSON.stringify(newPkgJson, null, 2).concat(\"\\n\");\n await fs.writeFile(pkgJsonPath, jsonString);\n}\n\n/** insert the exports field into the package.json */\nfunction insertExports(pkgJson: any, exports: Record<string, any>): any {\n // insert the export entries into the existing package.json, after these fields (for a clean format)\n const exportsAfter = [\n \"name\",\n \"description\",\n \"version\",\n \"private\",\n \"author\",\n \"type\",\n \"bin\",\n \"files\",\n \"repository\",\n \"homepage\",\n \"scripts\",\n ];\n const entries = Object.entries(pkgJson);\n const index = entries.findLastIndex(([key]) => exportsAfter.includes(key));\n\n entries.splice(index + 1, 0, [\"exports\", exports]);\n return Object.fromEntries(entries);\n}\n\n/** create one bundle per source module */\nasync function writeMultiBundle(\n modules: Record<string, string>,\n name: string,\n edition: string,\n projectDir: string,\n outDir: string,\n): Promise<void> {\n const sanitized = sanitizePackageName(name);\n for (const [moduleName, moduleSrc] of Object.entries(modules)) {\n const oneModule = { [moduleName]: moduleSrc };\n const moduleBundle: WeslBundle = {\n name: sanitized,\n edition,\n modules: oneModule,\n };\n const dependencies = parseDependencies(oneModule, projectDir);\n const bundleDirRelative = noSuffix(moduleName);\n const bundleDir = path.join(outDir, bundleDirRelative);\n await writeJsBundle(moduleBundle, dependencies, bundleDir);\n }\n}\n\n/** Write a weslBundle.js containing the bundled shader sources */\nasync function writeJsBundle(\n weslBundle: WeslBundle,\n dependencies: string[],\n outDir: string,\n): Promise<void> {\n await mkdir(outDir, { recursive: true });\n\n const depNames = dependencies.map(dep => dep.replaceAll(\"/\", \"_\"));\n const depsWithNames = zip(dependencies, depNames);\n\n const imports = depsWithNames\n .map(([dep, depName]) => {\n return `import ${depName} from \"${dep}\";`;\n })\n .join(\"\\n\");\n const importsStr = imports ? `${imports}\\n` : \"\";\n\n const bundleString = bundleToJsString(weslBundle, depNames);\n\n const outString = `\n ${importsStr}\n export const weslBundle = ${bundleString}\n\n export default weslBundle;\n `;\n\n const outPath = path.join(outDir, \"weslBundle.js\");\n const formatted = biome.formatContent(biomeKey, outString, {\n filePath: \"b.js\",\n });\n await fs.writeFile(outPath, formatted.content);\n}\n\n/** Write weslBundle.d.ts containing the type definitions for a WeslBundle */\nasync function writeTypeScriptDts(outDir: string): Promise<void> {\n // TODO could we use /// <reference types=\"wesl\"> to get the type of WeslBundle?\n const constDecl = `\n export declare const weslBundle: WeslBundle;\n export default weslBundle;\n `;\n const declText = weslBundleDecl + constDecl;\n const formatted = biome.formatContent(biomeKey, declText, {\n filePath: \"t.d.ts\",\n });\n\n const outPath = path.join(outDir, \"weslBundle.d.ts\");\n await fs.writeFile(outPath, formatted.content);\n}\n\n/** @return the bundle plus dependencies as a JavaScript string */\nfunction bundleToJsString(bundle: WeslBundle, dependencies: string[]): string {\n const { name, edition, modules } = bundle;\n const depsObj = dependencies.length ? { dependencies: 99 } : {};\n const obj = { name, edition, modules, ...depsObj };\n const jsonString = JSON.stringify(obj, null, 2);\n if (dependencies.length) {\n const dependenciesStr = `: [${dependencies.join(\", \")}]`;\n const result = jsonString.replace(\": 99\", dependenciesStr);\n return result;\n } else {\n return jsonString;\n }\n}\n\n/** Filter out self-dependencies from the dependency list */\nfunction filterSelfDeps(deps: string[], packageName: string): string[] {\n return deps.filter(\n dep => dep !== packageName && !dep.startsWith(`${packageName}/`),\n );\n}\n\ninterface PkgFields {\n name: string;\n}\n\n/** parse and extract fields from package.json that we care about\n * (the name of the package) */\nasync function loadPackageFields(pkgJsonPath: string): Promise<PkgFields> {\n const pkgJsonString = await fs.readFile(pkgJsonPath, { encoding: \"utf8\" });\n const pkgJson = JSON.parse(pkgJsonString);\n const { name } = pkgJson;\n verifyField(\"name\", name);\n\n function verifyField(field: string, value: any): void {\n if (value === undefined) {\n console.error(`no '${field}' field found in \"${pkgJsonPath}\"`);\n throw new Error(\"package.json incomplete\");\n }\n }\n return { name };\n}\n\n/** setup biome to use as a formatter */\nasync function setupBiome(): Promise<{ biome: Biome; projectKey: number }> {\n const biome = await Biome.create({\n distribution: Distribution.NODE,\n });\n const { projectKey } = biome.openProject();\n biome.applyConfiguration(projectKey, {\n formatter: {\n enabled: true,\n indentStyle: \"space\",\n lineEnding: \"lf\",\n expand: \"auto\",\n },\n });\n return { biome, projectKey };\n}\n","import { versionFromPackageJson } from \"wesl-tooling\";\nimport yargs from \"yargs\";\nimport { packageWgsl } from \"./PackageWesl.ts\";\n\nexport type CliArgs = Awaited<ReturnType<typeof parseArgs>>;\nlet cliArgs: CliArgs;\n\nexport async function packagerCli(rawArgs: string[]): Promise<void> {\n cliArgs = await parseArgs(rawArgs);\n await packageWgsl(cliArgs);\n}\n\nasync function parseArgs(args: string[]) {\n const projectDir = new URL(\"..\", import.meta.url).href;\n const appVersion = await versionFromPackageJson(projectDir);\n return yargs(args)\n .command(\"$0\", \"create an npm package from WGSL/WESL files\")\n .version(appVersion)\n .option(\"src\", {\n type: \"string\",\n describe:\n \"WGSL/WESL files to bundle in the package (glob syntax, defaults to wesl.toml or shaders/**/*.w[eg]sl)\",\n })\n .option(\"rootDir\", {\n deprecated: true,\n type: \"string\",\n describe: \"use --baseDir instead\",\n })\n .option(\"baseDir\", {\n deprecated: true,\n type: \"string\",\n describe: \"root directory for shaders (defaults to wesl.toml or shaders)\",\n })\n .option(\"projectDir\", {\n type: \"string\",\n default: \".\",\n describe: \"directory containing package.json and wesl.toml\",\n })\n .option(\"multiBundle\", {\n type: \"boolean\",\n default: false,\n describe: \"make a shader bundle for each source file\",\n })\n .option(\"exportName\", {\n type: \"string\",\n default: \".\",\n describe:\n \"package.json export name for consolidated bundle (ignored for multiBundle)\",\n })\n .option(\"updatePackageJson\", {\n type: \"boolean\",\n default: false,\n describe: \"add 'exports' entries into package.json\",\n })\n .option(\"outDir\", {\n type: \"string\",\n default: \"dist\",\n describe: \"where to put bundled output files (relative to projectDir)\",\n })\n .help()\n .parse();\n}\n","#!/usr/bin/env node\nimport { exit } from \"node:process\";\nimport { hideBin } from \"yargs/helpers\";\nimport { packagerCli } from \"./PackagerCli.ts\";\n\nconst rawArgs = hideBin(process.argv);\n\nconst [major] = process.versions.node.split(\".\").map(Number);\nif (major < 22) {\n console.error(\n `Please upgrade node to version 22 or higher. (The current node version is ${process.version})`,\n );\n exit(1);\n}\n\npackagerCli(rawArgs).catch(e => {\n console.error(e);\n exit(1);\n});\n"],"mappings":";;;;;;;;;;;;AAQA,MAAM,iBAAiB,MAAM,SAAS,IAAI,IAAI,kBAAkB,EAAE,OAAO;AAEzE,MAAM,EAAE,OAAO,YAAY,aAAa,MAAM,YAAY;;AAG1D,eAAsB,YAAY,MAA8B;CAC9D,MAAM,EAAE,YAAY,QAAQ,aAAa,SAAS,QAAQ;CAC1D,MAAM,UAAU,MAAM,YAAY,YAAY,SAAS,IAAI;CAE3D,MAAM,EAAE,SAAS,MAAM,kBADH,KAAK,KAAK,YAAY,eAAe,CACJ;CACrD,MAAM,UAAU;AAEhB,KAAI,KAAK,YACP,OAAM,iBAAiB,SAAS,MAAM,SAAS,YAAY,OAAO;MAC7D;EACL,MAAM,UAAU,kBAAkB,SAAS,WAAW;EACtD,MAAM,YAAY,oBAAoB,KAAK;EAC3C,MAAM,OAAO,eAAe,SAAS,UAAU;AAE/C,QAAM,cADqB;GAAE,MAAM;GAAW;GAAS;GAAS,EACpC,MAAM,OAAO;;AAE3C,OAAM,mBAAmB,OAAO;AAChC,KAAI,KAAK,kBACP,OAAM,kBAAkB,YAAY,QAAQ,YAAY;;;AAK5D,eAAe,kBACb,YACA,QACA,aACe;CACf,MAAM,cAAc,KAAK,KAAK,YAAY,eAAe;CACzD,MAAM,gBAAgB,MAAM,GAAG,SAAS,aAAa,EAAE,UAAU,QAAQ,CAAC;CAC1E,MAAM,UAAU,KAAK,MAAM,cAAc;CACzC,MAAM,UAA+B,EAAE;CAEvC,MAAM,UAAU,KAAK,SAAS,YAAY,OAAO;AACjD,KAAI,YACF,SAAQ,SAAS;EACf,OAAO,KAAK,QAAQ;EACpB,QAAQ,KAAK,QAAQ;EACtB;KAED,SAAQ,OAAO;EACb,OAAO,KAAK,QAAQ;EACpB,QAAQ,KAAK,QAAQ;EACtB;CAGH,MAAM,aAAa,cAAc,SAAS,QAAQ;CAClD,MAAM,aAAa,KAAK,UAAU,YAAY,MAAM,EAAE,CAAC,OAAO,KAAK;AACnE,OAAM,GAAG,UAAU,aAAa,WAAW;;;AAI7C,SAAS,cAAc,SAAc,SAAmC;CAEtE,MAAM,eAAe;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,MAAM,UAAU,OAAO,QAAQ,QAAQ;CACvC,MAAM,QAAQ,QAAQ,eAAe,CAAC,SAAS,aAAa,SAAS,IAAI,CAAC;AAE1E,SAAQ,OAAO,QAAQ,GAAG,GAAG,CAAC,WAAW,QAAQ,CAAC;AAClD,QAAO,OAAO,YAAY,QAAQ;;;AAIpC,eAAe,iBACb,SACA,MACA,SACA,YACA,QACe;CACf,MAAM,YAAY,oBAAoB,KAAK;AAC3C,MAAK,MAAM,CAAC,YAAY,cAAc,OAAO,QAAQ,QAAQ,EAAE;EAC7D,MAAM,YAAY,GAAG,aAAa,WAAW;EAC7C,MAAM,eAA2B;GAC/B,MAAM;GACN;GACA,SAAS;GACV;EACD,MAAM,eAAe,kBAAkB,WAAW,WAAW;EAC7D,MAAM,oBAAoB,SAAS,WAAW;AAE9C,QAAM,cAAc,cAAc,cADhB,KAAK,KAAK,QAAQ,kBAAkB,CACI;;;;AAK9D,eAAe,cACb,YACA,cACA,QACe;AACf,OAAM,MAAM,QAAQ,EAAE,WAAW,MAAM,CAAC;CAExC,MAAM,WAAW,aAAa,KAAI,QAAO,IAAI,WAAW,KAAK,IAAI,CAAC;CAGlE,MAAM,UAFgB,IAAI,cAAc,SAAS,CAG9C,KAAK,CAAC,KAAK,aAAa;AACvB,SAAO,UAAU,QAAQ,SAAS,IAAI;GACtC,CACD,KAAK,KAAK;CAKb,MAAM,YAAY;MAJC,UAAU,GAAG,QAAQ,MAAM,GAK/B;gCAHM,iBAAiB,YAAY,SAAS,CAIhB;;;;CAK3C,MAAM,UAAU,KAAK,KAAK,QAAQ,gBAAgB;CAClD,MAAM,YAAY,MAAM,cAAc,UAAU,WAAW,EACzD,UAAU,QACX,CAAC;AACF,OAAM,GAAG,UAAU,SAAS,UAAU,QAAQ;;;AAIhD,eAAe,mBAAmB,QAA+B;CAM/D,MAAM,WAAW,iBAJC;;;;CAKlB,MAAM,YAAY,MAAM,cAAc,UAAU,UAAU,EACxD,UAAU,UACX,CAAC;CAEF,MAAM,UAAU,KAAK,KAAK,QAAQ,kBAAkB;AACpD,OAAM,GAAG,UAAU,SAAS,UAAU,QAAQ;;;AAIhD,SAAS,iBAAiB,QAAoB,cAAgC;CAC5E,MAAM,EAAE,MAAM,SAAS,YAAY;CAEnC,MAAM,MAAM;EAAE;EAAM;EAAS;EAAS,GADtB,aAAa,SAAS,EAAE,cAAc,IAAI,GAAG,EAAE;EACb;CAClD,MAAM,aAAa,KAAK,UAAU,KAAK,MAAM,EAAE;AAC/C,KAAI,aAAa,QAAQ;EACvB,MAAM,kBAAkB,MAAM,aAAa,KAAK,KAAK,CAAC;AAEtD,SADe,WAAW,QAAQ,QAAQ,gBAAgB;OAG1D,QAAO;;;AAKX,SAAS,eAAe,MAAgB,aAA+B;AACrE,QAAO,KAAK,QACV,QAAO,QAAQ,eAAe,CAAC,IAAI,WAAW,GAAG,YAAY,GAAG,CACjE;;;;AASH,eAAe,kBAAkB,aAAyC;CACxE,MAAM,gBAAgB,MAAM,GAAG,SAAS,aAAa,EAAE,UAAU,QAAQ,CAAC;CAE1E,MAAM,EAAE,SADQ,KAAK,MAAM,cAAc;AAEzC,aAAY,QAAQ,KAAK;CAEzB,SAAS,YAAY,OAAe,OAAkB;AACpD,MAAI,UAAU,QAAW;AACvB,WAAQ,MAAM,OAAO,MAAM,oBAAoB,YAAY,GAAG;AAC9D,SAAM,IAAI,MAAM,0BAA0B;;;AAG9C,QAAO,EAAE,MAAM;;;AAIjB,eAAe,aAA4D;CACzE,MAAM,QAAQ,MAAM,MAAM,OAAO,EAC/B,cAAc,aAAa,MAC5B,CAAC;CACF,MAAM,EAAE,eAAe,MAAM,aAAa;AAC1C,OAAM,mBAAmB,YAAY,EACnC,WAAW;EACT,SAAS;EACT,aAAa;EACb,YAAY;EACZ,QAAQ;EACT,EACF,CAAC;AACF,QAAO;EAAE;EAAO;EAAY;;;;;ACpN9B,IAAI;AAEJ,eAAsB,YAAY,SAAkC;AAClE,WAAU,MAAM,UAAU,QAAQ;AAClC,OAAM,YAAY,QAAQ;;AAG5B,eAAe,UAAU,MAAgB;CACvC,MAAM,aAAa,IAAI,IAAI,MAAM,OAAO,KAAK,IAAI,CAAC;CAClD,MAAM,aAAa,MAAM,uBAAuB,WAAW;AAC3D,QAAO,MAAM,KAAK,CACf,QAAQ,MAAM,6CAA6C,CAC3D,QAAQ,WAAW,CACnB,OAAO,OAAO;EACb,MAAM;EACN,UACE;EACH,CAAC,CACD,OAAO,WAAW;EACjB,YAAY;EACZ,MAAM;EACN,UAAU;EACX,CAAC,CACD,OAAO,WAAW;EACjB,YAAY;EACZ,MAAM;EACN,UAAU;EACX,CAAC,CACD,OAAO,cAAc;EACpB,MAAM;EACN,SAAS;EACT,UAAU;EACX,CAAC,CACD,OAAO,eAAe;EACrB,MAAM;EACN,SAAS;EACT,UAAU;EACX,CAAC,CACD,OAAO,cAAc;EACpB,MAAM;EACN,SAAS;EACT,UACE;EACH,CAAC,CACD,OAAO,qBAAqB;EAC3B,MAAM;EACN,SAAS;EACT,UAAU;EACX,CAAC,CACD,OAAO,UAAU;EAChB,MAAM;EACN,SAAS;EACT,UAAU;EACX,CAAC,CACD,MAAM,CACN,OAAO;;;;;ACvDZ,MAAM,UAAU,QAAQ,QAAQ,KAAK;AAErC,MAAM,CAAC,SAAS,QAAQ,SAAS,KAAK,MAAM,IAAI,CAAC,IAAI,OAAO;AAC5D,IAAI,QAAQ,IAAI;AACd,SAAQ,MACN,6EAA6E,QAAQ,QAAQ,GAC9F;AACD,MAAK,EAAE;;AAGT,YAAY,QAAQ,CAAC,OAAM,MAAK;AAC9B,SAAQ,MAAM,EAAE;AAChB,MAAK,EAAE;EACP"}
|
|
1
|
+
{"version":3,"file":"main.mjs","names":[],"sources":["../src/PackageWesl.ts","../src/PackagerCli.ts","../src/main.ts"],"sourcesContent":["import fs, { mkdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { Biome, Distribution } from \"@biomejs/js-api\";\nimport { noSuffix, sanitizePackageName, type WeslBundle } from \"wesl\";\nimport { weslBundleDeclUrl } from \"wesl/bundle-decl\";\nimport { loadModules, parseDependencies, zip } from \"wesl-tooling\";\nimport type { CliArgs } from \"./PackagerCli.ts\";\n\nconst weslBundleDecl = await readFile(new URL(weslBundleDeclUrl), \"utf8\");\n\nconst { biome, projectKey: biomeKey } = await setupBiome();\n\n/** write weslBundle .js and .d.ts files for this shader */\nexport async function packageWgsl(args: CliArgs): Promise<void> {\n const { projectDir, outDir, multiBundle, baseDir, src } = args;\n const modules = await loadModules(projectDir, baseDir, src);\n const pkgJsonPath = path.join(projectDir, \"package.json\");\n const { name } = await loadPackageFields(pkgJsonPath);\n const edition = \"unstable_2025_1\";\n\n if (args.multiBundle) {\n await writeMultiBundle(modules, name, edition, projectDir, outDir);\n } else {\n const allDeps = parseDependencies(modules, projectDir);\n const sanitized = sanitizePackageName(name);\n const deps = filterSelfDeps(allDeps, sanitized);\n const bundle: WeslBundle = { name: sanitized, edition, modules };\n await writeJsBundle(bundle, deps, outDir);\n }\n await writeTypeScriptDts(outDir);\n if (args.updatePackageJson) {\n await updatePackageJson(projectDir, outDir, multiBundle);\n }\n}\n\n/** add an 'exports' entry to package.json for the wesl bundles */\nasync function updatePackageJson(\n projectDir: string,\n outDir: string,\n multiBundle: boolean,\n): Promise<void> {\n const pkgJsonPath = path.join(projectDir, \"package.json\");\n const pkgJsonString = await fs.readFile(pkgJsonPath, { encoding: \"utf8\" });\n const pkgJson = JSON.parse(pkgJsonString);\n const exports: Record<string, any> = {};\n\n const distDir = path.relative(projectDir, outDir);\n if (multiBundle) {\n exports[\"./*\"] = {\n types: `./${distDir}/weslBundle.d.ts`,\n import: `./${distDir}/*/weslBundle.js`,\n };\n } else {\n exports[\".\"] = {\n types: `./${distDir}/weslBundle.d.ts`,\n import: `./${distDir}/weslBundle.js`,\n };\n }\n\n const newPkgJson = insertExports(pkgJson, exports);\n const jsonString = JSON.stringify(newPkgJson, null, 2).concat(\"\\n\");\n await fs.writeFile(pkgJsonPath, jsonString);\n}\n\n/** insert the exports field into the package.json */\nfunction insertExports(pkgJson: any, exports: Record<string, any>): any {\n // insert the export entries into the existing package.json, after these fields (for a clean format)\n const exportsAfter = [\n \"name\",\n \"description\",\n \"version\",\n \"private\",\n \"author\",\n \"type\",\n \"bin\",\n \"files\",\n \"repository\",\n \"homepage\",\n \"scripts\",\n ];\n const entries = Object.entries(pkgJson);\n const index = entries.findLastIndex(([key]) => exportsAfter.includes(key));\n\n entries.splice(index + 1, 0, [\"exports\", exports]);\n return Object.fromEntries(entries);\n}\n\n/** create one bundle per source module */\nasync function writeMultiBundle(\n modules: Record<string, string>,\n name: string,\n edition: string,\n projectDir: string,\n outDir: string,\n): Promise<void> {\n const sanitized = sanitizePackageName(name);\n for (const [moduleName, moduleSrc] of Object.entries(modules)) {\n const oneModule = { [moduleName]: moduleSrc };\n const moduleBundle: WeslBundle = {\n name: sanitized,\n edition,\n modules: oneModule,\n };\n const dependencies = parseDependencies(oneModule, projectDir);\n const bundleDirRelative = noSuffix(moduleName);\n const bundleDir = path.join(outDir, bundleDirRelative);\n await writeJsBundle(moduleBundle, dependencies, bundleDir);\n }\n}\n\n/** Write a weslBundle.js containing the bundled shader sources */\nasync function writeJsBundle(\n weslBundle: WeslBundle,\n dependencies: string[],\n outDir: string,\n): Promise<void> {\n await mkdir(outDir, { recursive: true });\n\n const depNames = dependencies.map(dep => dep.replaceAll(\"/\", \"_\"));\n const depsWithNames = zip(dependencies, depNames);\n\n const imports = depsWithNames\n .map(([dep, depName]) => {\n return `import ${depName} from \"${dep}\";`;\n })\n .join(\"\\n\");\n const importsStr = imports ? `${imports}\\n` : \"\";\n\n const bundleString = bundleToJsString(weslBundle, depNames);\n\n const outString = `\n ${importsStr}\n export const weslBundle = ${bundleString}\n\n export default weslBundle;\n `;\n\n const outPath = path.join(outDir, \"weslBundle.js\");\n const formatted = biome.formatContent(biomeKey, outString, {\n filePath: \"b.js\",\n });\n await safeWrite(outPath, formatted.content);\n}\n\n/** Write weslBundle.d.ts containing the type definitions for a WeslBundle */\nasync function writeTypeScriptDts(outDir: string): Promise<void> {\n // TODO could we use /// <reference types=\"wesl\"> to get the type of WeslBundle?\n const constDecl = `\n export declare const weslBundle: WeslBundle;\n export default weslBundle;\n `;\n const declText = weslBundleDecl + constDecl;\n const formatted = biome.formatContent(biomeKey, declText, {\n filePath: \"t.d.ts\",\n });\n\n const outPath = path.join(outDir, \"weslBundle.d.ts\");\n await safeWrite(outPath, formatted.content);\n}\n\n/** Write without truncating the file (atomic via temp+rename).\n * Skips the write entirely if content is unchanged.\n * Avoids a potential race with parallel tsc readers during `--parallel` builds. */\nasync function safeWrite(filePath: string, content: string): Promise<void> {\n try {\n const existing = await fs.readFile(filePath, \"utf8\");\n if (existing === content) return;\n } catch {}\n const tmpPath = filePath + \".tmp\";\n await fs.writeFile(tmpPath, content);\n await fs.rename(tmpPath, filePath);\n}\n\n/** @return the bundle plus dependencies as a JavaScript string */\nfunction bundleToJsString(bundle: WeslBundle, dependencies: string[]): string {\n const { name, edition, modules } = bundle;\n const depsObj = dependencies.length ? { dependencies: 99 } : {};\n const obj = { name, edition, modules, ...depsObj };\n const jsonString = JSON.stringify(obj, null, 2);\n if (dependencies.length) {\n const dependenciesStr = `: [${dependencies.join(\", \")}]`;\n const result = jsonString.replace(\": 99\", dependenciesStr);\n return result;\n } else {\n return jsonString;\n }\n}\n\n/** Filter out self-dependencies from the dependency list */\nfunction filterSelfDeps(deps: string[], packageName: string): string[] {\n return deps.filter(\n dep => dep !== packageName && !dep.startsWith(`${packageName}/`),\n );\n}\n\ninterface PkgFields {\n name: string;\n}\n\n/** parse and extract fields from package.json that we care about\n * (the name of the package) */\nasync function loadPackageFields(pkgJsonPath: string): Promise<PkgFields> {\n const pkgJsonString = await fs.readFile(pkgJsonPath, { encoding: \"utf8\" });\n const pkgJson = JSON.parse(pkgJsonString);\n const { name } = pkgJson;\n verifyField(\"name\", name);\n\n function verifyField(field: string, value: any): void {\n if (value === undefined) {\n console.error(`no '${field}' field found in \"${pkgJsonPath}\"`);\n throw new Error(\"package.json incomplete\");\n }\n }\n return { name };\n}\n\n/** setup biome to use as a formatter */\nasync function setupBiome(): Promise<{ biome: Biome; projectKey: number }> {\n const biome = await Biome.create({\n distribution: Distribution.NODE,\n });\n const { projectKey } = biome.openProject();\n biome.applyConfiguration(projectKey, {\n formatter: {\n enabled: true,\n indentStyle: \"space\",\n lineEnding: \"lf\",\n expand: \"auto\",\n },\n });\n return { biome, projectKey };\n}\n","import { versionFromPackageJson } from \"wesl-tooling\";\nimport yargs from \"yargs\";\nimport { packageWgsl } from \"./PackageWesl.ts\";\n\nexport type CliArgs = Awaited<ReturnType<typeof parseArgs>>;\nlet cliArgs: CliArgs;\n\nexport async function packagerCli(rawArgs: string[]): Promise<void> {\n cliArgs = await parseArgs(rawArgs);\n await packageWgsl(cliArgs);\n}\n\nasync function parseArgs(args: string[]) {\n const projectDir = new URL(\"..\", import.meta.url).href;\n const appVersion = await versionFromPackageJson(projectDir);\n return yargs(args)\n .command(\"$0\", \"create an npm package from WGSL/WESL files\")\n .version(appVersion)\n .option(\"src\", {\n type: \"string\",\n describe:\n \"WGSL/WESL files to bundle in the package (glob syntax, defaults to wesl.toml or shaders/**/*.w[eg]sl)\",\n })\n .option(\"rootDir\", {\n deprecated: true,\n type: \"string\",\n describe: \"use --baseDir instead\",\n })\n .option(\"baseDir\", {\n deprecated: true,\n type: \"string\",\n describe: \"root directory for shaders (defaults to wesl.toml or shaders)\",\n })\n .option(\"projectDir\", {\n type: \"string\",\n default: \".\",\n describe: \"directory containing package.json and wesl.toml\",\n })\n .option(\"multiBundle\", {\n type: \"boolean\",\n default: false,\n describe: \"make a shader bundle for each source file\",\n })\n .option(\"exportName\", {\n type: \"string\",\n default: \".\",\n describe:\n \"package.json export name for consolidated bundle (ignored for multiBundle)\",\n })\n .option(\"updatePackageJson\", {\n type: \"boolean\",\n default: false,\n describe: \"add 'exports' entries into package.json\",\n })\n .option(\"outDir\", {\n type: \"string\",\n default: \"dist\",\n describe: \"where to put bundled output files (relative to projectDir)\",\n })\n .help()\n .parse();\n}\n","#!/usr/bin/env node\nimport { exit } from \"node:process\";\nimport { hideBin } from \"yargs/helpers\";\nimport { packagerCli } from \"./PackagerCli.ts\";\n\nconst rawArgs = hideBin(process.argv);\n\nconst [major] = process.versions.node.split(\".\").map(Number);\nif (major < 22) {\n console.error(\n `Please upgrade node to version 22 or higher. (The current node version is ${process.version})`,\n );\n exit(1);\n}\n\npackagerCli(rawArgs).catch(e => {\n console.error(e);\n exit(1);\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wesl-packager",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "dist/main.mjs",
|
|
6
6
|
"files": [
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"@biomejs/js-api": "^1.0.0",
|
|
14
14
|
"@biomejs/wasm-nodejs": "^2.0.6",
|
|
15
15
|
"yargs": "^18.0.0",
|
|
16
|
-
"wesl": "0.7.
|
|
17
|
-
"wesl-tooling": "0.6.
|
|
16
|
+
"wesl": "0.7.25",
|
|
17
|
+
"wesl-tooling": "0.6.37"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"dependent_package": "x",
|
package/src/PackageWesl.ts
CHANGED
|
@@ -139,7 +139,7 @@ async function writeJsBundle(
|
|
|
139
139
|
const formatted = biome.formatContent(biomeKey, outString, {
|
|
140
140
|
filePath: "b.js",
|
|
141
141
|
});
|
|
142
|
-
await
|
|
142
|
+
await safeWrite(outPath, formatted.content);
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
/** Write weslBundle.d.ts containing the type definitions for a WeslBundle */
|
|
@@ -155,7 +155,20 @@ async function writeTypeScriptDts(outDir: string): Promise<void> {
|
|
|
155
155
|
});
|
|
156
156
|
|
|
157
157
|
const outPath = path.join(outDir, "weslBundle.d.ts");
|
|
158
|
-
await
|
|
158
|
+
await safeWrite(outPath, formatted.content);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Write without truncating the file (atomic via temp+rename).
|
|
162
|
+
* Skips the write entirely if content is unchanged.
|
|
163
|
+
* Avoids a potential race with parallel tsc readers during `--parallel` builds. */
|
|
164
|
+
async function safeWrite(filePath: string, content: string): Promise<void> {
|
|
165
|
+
try {
|
|
166
|
+
const existing = await fs.readFile(filePath, "utf8");
|
|
167
|
+
if (existing === content) return;
|
|
168
|
+
} catch {}
|
|
169
|
+
const tmpPath = filePath + ".tmp";
|
|
170
|
+
await fs.writeFile(tmpPath, content);
|
|
171
|
+
await fs.rename(tmpPath, filePath);
|
|
159
172
|
}
|
|
160
173
|
|
|
161
174
|
/** @return the bundle plus dependencies as a JavaScript string */
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export interface WeslBundle {
|
|
2
|
-
/** npm package name sanitized to be a valid WESL identifier
|
|
3
|
-
* (@ removed, / ==> __, - ==> _) */
|
|
4
|
-
name: string;
|
|
5
|
-
|
|
6
|
-
/** WESL edition of the code e.g. unstable_2025_1 */
|
|
7
|
-
edition: string;
|
|
8
|
-
|
|
9
|
-
/** map of WESL/WGSL modules:
|
|
10
|
-
* keys are file paths, relative to package root (e.g. "./lib.wgsl")
|
|
11
|
-
* values are WESL/WGSL code strings
|
|
12
|
-
*/
|
|
13
|
-
modules: Record<string, string>;
|
|
14
|
-
|
|
15
|
-
/** packages referenced by this package */
|
|
16
|
-
dependencies?: WeslBundle[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export declare const weslBundle: WeslBundle;
|
|
20
|
-
export default weslBundle;
|