compress-shader-literals 0.0.4 → 0.0.7

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
@@ -1,15 +1,44 @@
1
1
  # compress-shader-literals
2
2
 
3
- > **Size matters.** Minify your GLSL & WGSL shader strings at build time — automatically, in any bundler.
3
+ > **Size matters.** Strip everything strippable from your GLSL & WGSL shaders at build time.
4
+
5
+ > ⭐ Star this repository to support development and help others discover it.
4
6
 
5
7
  [![npm version](https://img.shields.io/npm/v/compress-shader-literals?color=cb3837&logo=npm)](https://www.npmjs.com/package/compress-shader-literals)
6
8
  [![minzipped size](https://img.shields.io/bundlephobia/minzip/compress-shader-literals?color=success)](https://bundlephobia.com/package/compress-shader-literals)
7
9
  [![npm downloads](https://img.shields.io/npm/dm/compress-shader-literals?color=success)](https://www.npmjs.com/package/compress-shader-literals)
8
10
  [![license](https://img.shields.io/npm/l/compress-shader-literals?color=blue)](./LICENSE)
9
11
 
10
- Your shaders ship with every comment, indent, and blank line you wrote them with. This strips all of it — block comments, line comments, redundant whitespace from `glsl` / `wgsl` template literals, **before** your bundler minifies the rest. Smaller bundles, zero source changes, full sourcemaps.
12
+ Your shaders ship to users as strings. This strips the comments and whitespace out of them at build time, before your bundler ever sees them in any bundler.
13
+
14
+ ![Alt Preview](./assets/preview.png)
15
+
16
+ ## Stats
17
+
18
+ Measured on the real shaders shipped by popular libraries:
19
+
20
+ <!-- STATS:START -->
21
+
22
+ | Package | Shaders | Before | After | Saved |
23
+ | --------------------- | ------: | --------: | --------: | --------: |
24
+ | `three` | 281 | 240,772 B | 203,428 B | **15.5%** |
25
+ | `@jayf0x/fluidity-js` | 9 | 9,524 B | 7,133 B | **25.1%** |
26
+ | `ogl` | 22 | 6,109 B | 5,335 B | **12.7%** |
27
+ | `shader-park-core` | 18 | 10,794 B | 9,033 B | **16.3%** |
28
+ | `curtainsjs` | 7 | 3,406 B | 2,563 B | **24.8%** |
29
+ | **Total** | 337 | 270,605 B | 227,492 B | **15.9%** |
30
+
31
+ _Auto-generated by [`tests/e2e.js`](tests/e2e.js) · packages [verified](tests/validate.js) loadable · 2026-06-24_
32
+
33
+ <!-- STATS:END -->
11
34
 
12
- Built on [unplugin](https://github.com/unjs/unplugin), so it runs in **Vite, Rollup, webpack, esbuild, Rspack, Rolldown & Farm** from one package.
35
+ <!-- <a href="https://star-history.com/#jayf0x/compress-shader-literals&Date">
36
+ <picture>
37
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=jayf0x/compress-shader-literals&type=Date&theme=dark&legend=top-left" />
38
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=jayf0x/compress-shader-literals&type=Date&legend=top-left" />
39
+ <img alt="Star History Chart" src="https://api.star-history.com/chart?repos=jayf0x/compress-shader-literals&type=Date&legend=top-left" />
40
+ </picture>
41
+ </a> -->
13
42
 
14
43
  ## Install
15
44
 
@@ -34,74 +63,56 @@ export default { plugins: [compressShaderLiterals.vite({ outputRatio: true })] }
34
63
  // Rspack / Rolldown / Farm → .rspack() / .rolldown() / .farm()
35
64
  ```
36
65
 
37
- That's it. Shaders are compressed on the way through; everything else is untouched.
38
-
39
- ## What it compresses
66
+ It finds tagged and comment-prefixed literals — and yes, the comment form keeps your editor's syntax highlighting:
40
67
 
41
68
  ```ts
42
- // Tagged template literal
43
69
  const vert = glsl`
44
70
  // vertex shader ← stripped
45
71
  precision highp float;
46
72
  void main() { gl_Position = vec4(0.0); }
47
73
  `;
48
74
 
49
- // Comment-prefixed template literal (great for editor syntax highlighting)
50
75
  const frag = /* wgsl */ `
51
76
  /* fragment */
52
77
  fn main() {}
53
78
  `;
54
79
  ```
55
80
 
56
- both become a single tight line, no comments, no padding.
57
-
58
- ## Options
59
-
60
- | Option | Default | Description |
61
- | ------------- | ---------------------------- | --------------------------------------- |
62
- | `tags` | `['glsl', 'wgsl', 'shader']` | Tag names / comment markers to match |
63
- | `include` | `[/\.[jt]sx?$/]` | Files to process |
64
- | `exclude` | `[/node_modules/, /dist/]` | Files to skip |
65
- | `outputRatio` | `false` | Print a bytes-saved summary after build |
66
-
67
- With `outputRatio: true`:
68
-
69
- ```
70
- compress-shader-literals
71
- ────────────────────────
72
- 215.00 B → 134.00 B
73
- saved: 81.00 B (37.67% smaller)
74
- shaders: 1
75
- ```
76
-
77
- ## Real-world stats
81
+ Both collapse to a single tight line. No comments, no padding, no touched source files.
78
82
 
79
- Run against the actual shaders shipped in **[three.js](https://www.npmjs.com/package/three)**, **[ogl](https://www.npmjs.com/package/ogl)**, **[shader-park-core](https://www.npmjs.com/package/shader-park-core)**, **[curtains.js](https://www.npmjs.com/package/curtainsjs)** and more — measured by this plugin's own engine ([`tests/e2e.js`](tests/e2e.js)):
83
+ **Options**
80
84
 
81
- <!-- STATS:START -->
85
+ | Option | Default | Description |
86
+ | ------------- | ---------------------------- | -------------------------------------------------- |
87
+ | `tags` | `['glsl', 'wgsl', 'shader']` | Tag names / comment markers to match |
88
+ | `include` | `[/\.[jt]sx?$/]` | Files to process |
89
+ | `exclude` | `[/node_modules/, /dist/]` | Files to skip |
90
+ | `outputRatio` | `false` | Print a bytes-saved summary after build |
91
+ | `transform` | built-in `minifyShader` | Custom minifier — `(shader: string) => string` |
92
+ | `debug` | `false` | Log each file's discovered literals to the console |
82
93
 
83
- | Package | Shaders | Before | After | Saved |
84
- | --------------------- | ------: | --------: | --------: | --------: |
85
- | `three` | 281 | 240,772 B | 203,428 B | **15.5%** |
86
- | `@jayf0x/fluidity-js` | 9 | 9,524 B | 7,133 B | **25.1%** |
87
- | `ogl` | 22 | 6,109 B | 5,335 B | **12.7%** |
88
- | `shader-park-core` | 18 | 10,794 B | 9,033 B | **16.3%** |
89
- | `curtainsjs` | 7 | 3,406 B | 2,563 B | **24.8%** |
90
- | **Total** | 337 | 270,605 B | 227,492 B | **15.9%** |
94
+ **Programmatic API** the two core helpers are exported for tooling authors (validators, ESLint rules, CLIs), no plugin required:
91
95
 
92
- _337 real shaders from 5 package(s), compressed by this plugin's engine._
96
+ ```js
97
+ import { extractShaderLiterals, minifyShader } from 'compress-shader-literals';
93
98
 
94
- <!-- STATS:END -->
99
+ extractShaderLiterals('const v = glsl`void main() {}`');
100
+ // → [{ tag: 'glsl', value: 'void main() {}', start: 10, end: 26 }]
95
101
 
96
- Free bytes off code you already ship — no source changes. Marginal per shader, but it compounds across every shader in every bundle, and at infra scale even 1% is worth shipping.
102
+ minifyShader('// comment\nvoid main() {}'); // 'void main() {}'
103
+ ```
97
104
 
98
105
  ## How it works
99
106
 
107
+ Nothing clever, which is the point:
108
+
100
109
  1. Parses each matched file with Babel (TS/JSX aware).
101
- 2. Finds tagged and comment-prefixed shader literals (see [What it compresses](#what-it-compresses)).
102
- 3. Strips comments + collapses whitespace, rewriting via [`magic-string`](https://github.com/Rich-Harris/magic-string) so **sourcemaps stay intact**.
110
+ 2. Finds tagged and comment-prefixed shader literals.
111
+ 3. Strips comments + collapses whitespace via [`magic-string`](https://github.com/Rich-Harris/magic-string), so **sourcemaps stay intact**.
103
112
  4. Runs as a `pre` transform, so your bundler's own minifier still sees the result.
104
113
 
114
+ One [unplugin](https://github.com/unjs/unplugin) covers **Vite, Rollup, webpack, esbuild, Rspack, Rolldown & Farm**.
115
+
105
116
  ## License
106
117
 
107
- [MIT](./LICENSE) © jayF0x
118
+ [MIT](./LICENSE) © [jayF0x](https://github.com/jayf0x)
@@ -0,0 +1,41 @@
1
+ import type { UnpluginInstance } from 'unplugin';
2
+
3
+ /** A picomatch glob string, RegExp, or a list of them — matched against module ids. */
4
+ type FilterPattern = string | RegExp | ReadonlyArray<string | RegExp> | null;
5
+
6
+ export interface CompressShaderLiteralsOptions {
7
+ /** Tag names / comment markers to match. Default: `['glsl', 'wgsl', 'shader']` */
8
+ tags?: string[];
9
+ /** Files to process. Default: `[/\.[jt]sx?$/]` */
10
+ include?: FilterPattern;
11
+ /** Files to skip. Default: `[/node_modules/, /dist/]` */
12
+ exclude?: FilterPattern;
13
+ /** Print a bytes-saved summary after build. Default: `false` */
14
+ outputRatio?: boolean;
15
+ /** Custom minifier, replaces the built-in `minifyShader`. Receives the raw literal, returns the transformed source. */
16
+ transform?: (shader: string) => string;
17
+ /** Log each file's discovered literals to the console. Default: `false` */
18
+ debug?: boolean;
19
+ }
20
+
21
+ /** A shader literal discovered in source. */
22
+ export interface ShaderLiteral {
23
+ tag: string;
24
+ value: string;
25
+ start: number;
26
+ end: number;
27
+ }
28
+
29
+ /**
30
+ * Minify GLSL/WGSL shader template literals at build time.
31
+ *
32
+ * Universal plugin — call the bundler you use: `.vite()`, `.rollup()`,
33
+ * `.webpack()`, `.esbuild()`, `.rspack()`, `.rolldown()`, `.farm()`.
34
+ */
35
+ export declare const compressShaderLiterals: UnpluginInstance<CompressShaderLiteralsOptions | undefined, boolean>;
36
+
37
+ /** Find tagged (`glsl\`...\``) and comment-prefixed (`/* wgsl *\/ \`...\``) shader literals in source. */
38
+ export declare function extractShaderLiterals(code: string, tags?: string[]): ShaderLiteral[];
39
+
40
+ /** Strip comments and collapse whitespace in a shader source string. */
41
+ export declare function minifyShader(src: string): string;
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/plugin.js
2
2
  import { createFilter } from "@rollup/pluginutils";
3
+ import { diff, snap } from "byte-snap";
3
4
  import MagicString from "magic-string";
4
5
  import { createUnplugin } from "unplugin";
5
6
 
@@ -62,31 +63,12 @@ function minifyShader(src) {
62
63
  }
63
64
 
64
65
  // src/plugin.js
65
- function fmtBytes(bytes) {
66
- const units = ["B", "KB", "MB", "GB"];
67
- let v = bytes;
68
- let u = 0;
69
- while (v >= 1024 && u < units.length - 1) {
70
- v /= 1024;
71
- u++;
72
- }
73
- return `${v.toFixed(2)} ${units[u]}`;
74
- }
75
- function printRatio(beforeBytes, afterBytes, files) {
76
- const saved = beforeBytes - afterBytes;
77
- const pct = beforeBytes === 0 ? 0 : saved / beforeBytes * 100;
78
- console.log(`
79
- compress-shader-literals`);
80
- console.log("────────────────────────");
81
- console.log(`${fmtBytes(beforeBytes)} → ${fmtBytes(afterBytes)}`);
82
- console.log(`saved: ${fmtBytes(saved)} (${pct.toFixed(2)}% smaller)`);
83
- console.log(`shaders: ${files}
84
- `);
85
- }
86
66
  var compressShaderLiterals = createUnplugin((options = {}) => {
87
67
  const tags = options.tags || ["glsl", "wgsl", "shader"];
68
+ const minify = options.transform || minifyShader;
88
69
  const filter = createFilter(options.include || [/\.[jt]sx?$/], options.exclude || [/node_modules/, /dist/]);
89
- const stats = { before: 0, after: 0, files: 0 };
70
+ let beforeText = "";
71
+ let afterText = "";
90
72
  return {
91
73
  name: "compress-shader-literals",
92
74
  enforce: "pre",
@@ -98,31 +80,32 @@ var compressShaderLiterals = createUnplugin((options = {}) => {
98
80
  const literals = extractShaderLiterals(code, tags);
99
81
  if (literals.length === 0)
100
82
  return null;
83
+ if (options.debug) {
84
+ console.log(`[compress-shader-literals] ${id}: ${literals.length} literal(s) — ${literals.map((l) => l.tag).join(", ")}`);
85
+ }
101
86
  const ms = new MagicString(code);
102
87
  let hasChanges = false;
103
88
  for (const literal of literals) {
104
- const minified = minifyShader(literal.value);
89
+ const minified = minify(literal.value);
105
90
  if (literal.value !== minified) {
106
91
  ms.overwrite(literal.start, literal.end, `\`${minified}\``);
107
92
  hasChanges = true;
108
93
  }
94
+ if (options.outputRatio) {
95
+ beforeText += literal.value;
96
+ afterText += minified;
97
+ }
109
98
  }
110
99
  if (!hasChanges)
111
100
  return null;
112
- const resultCode = ms.toString();
113
- if (options.outputRatio) {
114
- stats.before += Buffer.byteLength(code);
115
- stats.after += Buffer.byteLength(resultCode);
116
- stats.files += 1;
117
- }
118
101
  return {
119
- code: resultCode,
102
+ code: ms.toString(),
120
103
  map: ms.generateMap({ hires: true, source: id })
121
104
  };
122
105
  },
123
106
  buildEnd() {
124
- if (options.outputRatio && stats.files > 0) {
125
- printRatio(stats.before, stats.after, stats.files);
107
+ if (options.outputRatio && beforeText) {
108
+ diff(snap.text(beforeText), snap.text(afterText)).print();
126
109
  }
127
110
  }
128
111
  };
package/dist/index.js.br CHANGED
Binary file
package/dist/index.js.gz CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compress-shader-literals",
3
- "version": "0.0.4",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "description": "Size matters. Minify GLSL/WGSL shader template literals at build time — universal plugin for Vite, Rollup, webpack, esbuild & more.",
6
6
  "keywords": [
@@ -8,7 +8,13 @@
8
8
  "glsl",
9
9
  "wgsl",
10
10
  "minify",
11
+ "minification",
11
12
  "compress",
13
+ "compression",
14
+ "bundle-size",
15
+ "optimization",
16
+ "code-size",
17
+ "typescript",
12
18
  "webgl",
13
19
  "webgpu",
14
20
  "three",
@@ -36,8 +42,10 @@
36
42
  },
37
43
  "main": "./dist/index.js",
38
44
  "module": "./dist/index.js",
45
+ "types": "./dist/index.d.ts",
39
46
  "exports": {
40
47
  ".": {
48
+ "types": "./dist/index.d.ts",
41
49
  "import": "./dist/index.js"
42
50
  }
43
51
  },
@@ -46,9 +54,11 @@
46
54
  ],
47
55
  "sideEffects": false,
48
56
  "scripts": {
49
- "build": "bun build src/plugin.js --outdir dist --entry-naming index.js --target node --format esm --external @babel/parser --external @babel/traverse --external @rollup/pluginutils --external magic-string --external unplugin && node scripts/compress-dist.js",
57
+ "build": "bun build src/plugin.js --outdir dist --entry-naming index.js --target node --format esm --external @babel/parser --external @babel/traverse --external @rollup/pluginutils --external byte-snap --external magic-string --external unplugin && cp src/index.d.ts dist/index.d.ts && node scripts/compress-dist.js",
58
+ "typecheck": "tsc --noEmit --strict --skipLibCheck src/index.d.ts",
50
59
  "test": "bun test",
51
60
  "test:run": "bun test",
61
+ "test:validate": "cd tests && bun install && node validate.js",
52
62
  "test:e2e": "cd tests && bun install && node e2e.js",
53
63
  "format": "prettier --write .",
54
64
  "format:check": "prettier --check .",
@@ -59,11 +69,13 @@
59
69
  "@babel/parser": "^8.0.0",
60
70
  "@babel/traverse": "^8.0.0",
61
71
  "@rollup/pluginutils": "^5.4.0",
72
+ "byte-snap": "^1.0.3",
62
73
  "magic-string": "^0.30.21",
63
74
  "unplugin": "^3.0.0"
64
75
  },
65
76
  "devDependencies": {
66
77
  "@trivago/prettier-plugin-sort-imports": "^5.0.0",
67
- "prettier": "^3.0.0"
78
+ "prettier": "^3.0.0",
79
+ "typescript": "^5.6.0"
68
80
  }
69
81
  }