vanilla-jet 1.5.1 → 1.5.3

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/CHANGELOG.md CHANGED
@@ -4,6 +4,33 @@ All notable project changes are documented in this file.
4
4
 
5
5
  The format follows a structure inspired by Keep a Changelog and semantic versioning.
6
6
 
7
+ ## [1.5.3] - 2026-06-28
8
+
9
+ ### Changed
10
+
11
+ - **Static compression now applies to ALL compressible assets, not just the app bundle.**
12
+ `framework/router.js` negotiates `.br`/`.gz` for any `css`/`js` request that has a precompressed
13
+ sibling (falling back to the original otherwise) — previously only `vanilla.min.js`/`app.min.css`.
14
+ This makes self-hosted vendor libraries serve gzip/brotli instead of uncompressed.
15
+ - `scripts/compress_br.js` now walks `public/scripts`, `public/styles`, `public/pages` and emits
16
+ `.gz` + `.br` for every `.js`/`.css`/`.html` (was: only three named files).
17
+
18
+ ### Why
19
+
20
+ - Self-hosting third-party libs (to cut CDN origins) only helps if they're served compressed;
21
+ otherwise a large lib (e.g. a 369 KB bundle) would ship uncompressed and regress vs the CDN.
22
+
23
+ ## [1.5.2] - 2026-06-28
24
+
25
+ ### Added
26
+
27
+ - **Brotli precompression** of build outputs (`scripts/compress_br.js` + Gulp `compressBr`): generates
28
+ `.br` for `vanilla.min.js`, `app.min.css` and `public/pages/home.html`. The server already negotiates
29
+ `.br -> .gz -> original` when `settings.profile.enable_precompressed_negotiation` is true and the client
30
+ sends `Accept-Encoding: br`. Additive and safe: clients that don't accept brotli keep getting gzip.
31
+ - **`settings.profile.defer_scripts`** (default `false`): when enabled, `dipper.includeScript()` adds
32
+ `defer` to non-async scripts so they don't block HTML parsing (document order preserved). Opt-in.
33
+
7
34
  ## [1.5.1] - 2026-06-28
8
35
 
9
36
  ### Changed
@@ -307,7 +307,9 @@ Dipper.prototype.includeScript = function(script) {
307
307
  //type = item['cdn'] ? "" : 'type=\"text/javascript\"',
308
308
  resource = item['resource'],
309
309
  isAsync = item['async'] ? ' async' : '',
310
- defer = item['defer'] ? ' defer' : '',
310
+ // Honor a per-script defer flag, or the global settings.profile.defer_scripts
311
+ // (applied to non-async scripts so they don't block parsing; order preserved).
312
+ defer = (item['defer'] || (obj.options && obj.options.defer_scripts && !item['async'])) ? ' defer' : '',
311
313
  origin = item['origin'] != '' ? ' crossorigin=\"' + item['origin'] + '\"' : '',
312
314
  integrity = item['integrity'] != '' ? ' integrity=\"' + item['integrity'] + '\"' : '';
313
315
 
@@ -199,7 +199,11 @@ class Router {
199
199
  getStaticCandidates(request, ext, filename) {
200
200
  let obj = this;
201
201
  let candidates = [{ filename: filename, contentEncoding: '' }];
202
- let isCompressible = obj.compressionMimes.includes(ext) && obj.compressionFiles.includes(path.basename(filename));
202
+ // Any compressible type can be served precompressed; resolveFirstAvailableStaticFile
203
+ // falls back to the original when a .br/.gz sibling doesn't exist. This lets ALL
204
+ // self-hosted assets (vendor libs, plugins, …) be served gzip/brotli, not just the
205
+ // app bundle — otherwise self-hosting a large lib would ship it uncompressed.
206
+ let isCompressible = obj.compressionMimes.includes(ext);
203
207
  if (!isCompressible) {
204
208
  return candidates;
205
209
  }
@@ -37,6 +37,7 @@ class Server {
37
37
  wsServer: false,
38
38
  enable_precompressed_negotiation: false,
39
39
  enable_service_worker: false,
40
+ defer_scripts: false,
40
41
  request_timeout_ms: 30000,
41
42
  headers_timeout_ms: 35000,
42
43
  keep_alive_timeout_ms: 5000
package/gulpfile.js CHANGED
@@ -127,6 +127,12 @@ function generateServiceWorker() {
127
127
  .pipe(shell([`node scripts/generate_sw.js ${buildEnv}`]));
128
128
  }
129
129
 
130
+ // Brotli precompression of build outputs (served via Accept-Encoding negotiation)
131
+ function compressBr() {
132
+ return gulp.src('.')
133
+ .pipe(shell([`node scripts/compress_br.js`]));
134
+ }
135
+
130
136
  // Watch task
131
137
  function watchFiles(cb) {
132
138
  livereload.listen();
@@ -168,7 +174,8 @@ const build = gulp.series(
168
174
  buildLess,
169
175
  compileTemplates,
170
176
  gulp.parallel(compressJs, compressCss),
171
- generateServiceWorker
177
+ generateServiceWorker,
178
+ compressBr
172
179
  );
173
180
 
174
181
  const dev = gulp.series(
@@ -186,6 +193,7 @@ exports.compressJs = compressJs;
186
193
  exports.compressCss = compressCss;
187
194
  exports.compileTemplates = compileTemplates;
188
195
  exports.generateServiceWorker = generateServiceWorker;
196
+ exports.compressBr = compressBr;
189
197
  exports.build = build;
190
198
  exports.dev = dev;
191
199
  exports.default = dev;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-jet",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "VannilaJet framework",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,59 @@
1
+ // Precompresses build outputs to .gz + .br so the server can serve any self-hosted
2
+ // asset (vendor libs, plugins, bundles, styles, pages) compressed via Accept-Encoding
3
+ // negotiation. Without this, self-hosting a large library would ship it uncompressed.
4
+ // Safe + additive: clients that don't accept br get gzip; those that accept neither
5
+ // get the original (resolveFirstAvailableStaticFile falls back).
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const zlib = require('zlib');
10
+
11
+ function processCwd() {
12
+ return process.cwd()
13
+ .replace('/scripts', '')
14
+ .replace('/gulp', '')
15
+ .replace('/node_modules/vanilla-jet', '');
16
+ }
17
+
18
+ const root = processCwd();
19
+ const DIRS = ['public/scripts', 'public/styles', 'public/pages'];
20
+ const COMPRESSIBLE = new Set(['.js', '.css', '.html']);
21
+ const BROTLI_OPTIONS = { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 } };
22
+
23
+ function walk(dir, out) {
24
+ let entries;
25
+ try {
26
+ entries = fs.readdirSync(dir, { withFileTypes: true });
27
+ } catch (err) {
28
+ return;
29
+ }
30
+ for (const entry of entries) {
31
+ const full = path.join(dir, entry.name);
32
+ if (entry.isDirectory()) {
33
+ walk(full, out);
34
+ } else if (
35
+ COMPRESSIBLE.has(path.extname(entry.name)) &&
36
+ !entry.name.endsWith('.gz') &&
37
+ !entry.name.endsWith('.br')
38
+ ) {
39
+ out.push(full);
40
+ }
41
+ }
42
+ }
43
+
44
+ const files = [];
45
+ DIRS.forEach((dir) => walk(path.join(root, dir), files));
46
+
47
+ let count = 0;
48
+ files.forEach((file) => {
49
+ try {
50
+ const input = fs.readFileSync(file);
51
+ fs.writeFileSync(file + '.gz', zlib.gzipSync(input, { level: 9 }));
52
+ fs.writeFileSync(file + '.br', zlib.brotliCompressSync(input, BROTLI_OPTIONS));
53
+ count = count + 1;
54
+ } catch (err) {
55
+ // Skip unreadable files; never fail the build over compression.
56
+ }
57
+ });
58
+
59
+ console.log(`VanillaJet - precompressed ${count} assets (.gz + .br)`);