vanilla-jet 1.5.0 → 1.5.2

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,32 @@ 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.2] - 2026-06-28
8
+
9
+ ### Added
10
+
11
+ - **Brotli precompression** of build outputs (`scripts/compress_br.js` + Gulp `compressBr`): generates
12
+ `.br` for `vanilla.min.js`, `app.min.css` and `public/pages/home.html`. The server already negotiates
13
+ `.br -> .gz -> original` when `settings.profile.enable_precompressed_negotiation` is true and the client
14
+ sends `Accept-Encoding: br`. Additive and safe: clients that don't accept brotli keep getting gzip.
15
+ - **`settings.profile.defer_scripts`** (default `false`): when enabled, `dipper.includeScript()` adds
16
+ `defer` to non-async scripts so they don't block HTML parsing (document order preserved). Opt-in.
17
+
18
+ ## [1.5.1] - 2026-06-28
19
+
20
+ ### Changed
21
+
22
+ - **Service worker precache is now fully derived from `vanillaJet.package.json`.** `scripts/generate_sw.js`
23
+ reads the Dipper's full local registry (`coreDependencies` + `dependencies` + `styles` that resolve to
24
+ `/public/...`) instead of only the enqueued subset, so first-party assets like `coreLib/*` are precached
25
+ automatically. Consumers no longer need to hardcode a `service_worker.precache` list.
26
+ - Added `service_worker.precache_exclude` (opt-out by path) for declared assets you don't want cached.
27
+ `service_worker.precache` remains as an optional escape hatch for extras not declared in the package file.
28
+
29
+ ### Compatibility notes
30
+
31
+ - Backward compatible: an explicit `service_worker.precache` still works (merged with the derived set).
32
+
7
33
  ## [1.5.0] - 2026-06-27
8
34
 
9
35
  ### Added
@@ -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
 
@@ -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.0",
3
+ "version": "1.5.2",
4
4
  "description": "VannilaJet framework",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,46 @@
1
+ // Brotli-precompresses the build outputs so the server can negotiate `.br`
2
+ // (smaller than gzip). Safe + additive: the framework only serves `.br` when the
3
+ // client sends `Accept-Encoding: br`; otherwise it falls back to `.gz`/original.
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const zlib = require('zlib');
8
+
9
+ function processCwd() {
10
+ return process.cwd()
11
+ .replace('/scripts', '')
12
+ .replace('/gulp', '')
13
+ .replace('/node_modules/vanilla-jet', '');
14
+ }
15
+
16
+ const root = processCwd();
17
+
18
+ // Same files the framework negotiates `.br` for (router static + response.render).
19
+ const TARGETS = [
20
+ 'public/scripts/vanilla.min.js',
21
+ 'public/styles/app.min.css',
22
+ 'public/pages/home.html'
23
+ ];
24
+
25
+ const BROTLI_OPTIONS = {
26
+ params: {
27
+ [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
28
+ [zlib.constants.BROTLI_PARAM_SIZE_HINT]: 0
29
+ }
30
+ };
31
+
32
+ TARGETS.forEach((relativePath) => {
33
+ const filePath = path.join(root, relativePath);
34
+ try {
35
+ const stats = fs.statSync(filePath);
36
+ if (!stats.isFile()) {
37
+ return;
38
+ }
39
+ const input = fs.readFileSync(filePath);
40
+ const compressed = zlib.brotliCompressSync(input, BROTLI_OPTIONS);
41
+ fs.writeFileSync(filePath + '.br', compressed);
42
+ console.log(`VanillaJet - brotli: ${relativePath}.br (${input.length} -> ${compressed.length} B)`);
43
+ } catch (err) {
44
+ // File not present (feature not built yet) — skip silently.
45
+ }
46
+ });
@@ -63,9 +63,11 @@ function isLocalPublicPath(url) {
63
63
  return typeof url === 'string' && url.startsWith('/public/') && !url.startsWith('//');
64
64
  }
65
65
 
66
- // Best-effort: read the resources the Dipper enqueues for this app, so consumer
67
- // plugins/styles get precached automatically. Any failure falls back to core only.
68
- function deriveEnqueuedAssets(root, opts, shared) {
66
+ // Source of truth = vanillaJet.package.json. We hydrate the Dipper and read its full
67
+ // registry (coreDependencies + dependencies + styles), keeping every LOCAL resource.
68
+ // This way the precache list is derived from the declared deps, with no raw paths in
69
+ // the consumer config. Any failure falls back to core only.
70
+ function deriveLocalAssets(root, opts, shared) {
69
71
  try {
70
72
  const Dipper = require('../framework/dipper.js');
71
73
  const Functions = require('../framework/functions.js');
@@ -73,16 +75,16 @@ function deriveEnqueuedAssets(root, opts, shared) {
73
75
  Functions.hydrate(dipper);
74
76
 
75
77
  const assets = [];
76
- const collect = (registry, enqueued) => {
77
- Object.keys(enqueued || {}).forEach((name) => {
78
+ const collect = (registry) => {
79
+ Object.keys(registry || {}).forEach((name) => {
78
80
  const entry = registry[name];
79
81
  if (entry && isLocalPublicPath(entry.resource)) {
80
82
  assets.push(stripQuery(entry.resource));
81
83
  }
82
84
  });
83
85
  };
84
- collect(dipper.styles, dipper.enqueued_styles);
85
- collect(dipper.scripts, dipper.enqueued_scripts);
86
+ collect(dipper.styles);
87
+ collect(dipper.scripts);
86
88
  return assets;
87
89
  } catch (err) {
88
90
  return [];
@@ -90,15 +92,18 @@ function deriveEnqueuedAssets(root, opts, shared) {
90
92
  }
91
93
 
92
94
  function buildPrecacheList(root, opts, shared) {
93
- const configured = (opts.service_worker && Array.isArray(opts.service_worker.precache))
94
- ? opts.service_worker.precache
95
- : [];
95
+ const sw = opts.service_worker || {};
96
+ // `precache` = optional extras NOT declared in vanillaJet.package.json.
97
+ const configured = Array.isArray(sw.precache) ? sw.precache : [];
98
+ // `precache_exclude` = opt-out for declared-but-don't-cache assets (heavy/rare).
99
+ const exclude = (Array.isArray(sw.precache_exclude) ? sw.precache_exclude : []).map(stripQuery);
96
100
 
97
101
  const candidates = CORE_PRECACHE
98
- .concat(deriveEnqueuedAssets(root, opts, shared))
102
+ .concat(deriveLocalAssets(root, opts, shared))
99
103
  .concat(configured)
100
104
  .map(stripQuery)
101
- .filter(isLocalPublicPath);
105
+ .filter(isLocalPublicPath)
106
+ .filter((assetPath) => !exclude.includes(assetPath));
102
107
 
103
108
  const seen = new Set();
104
109
  const precache = [];