vanilla-jet 1.4.1 → 1.4.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,12 @@ 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.4.2] - 2026-02-19
8
+
9
+ ### Changed
10
+
11
+ - Version bump to 1.4.2.
12
+
7
13
  ## [1.4.1] - 2026-02-19
8
14
 
9
15
  ### Highlights (v1.4.1)
@@ -11,9 +17,11 @@ The format follows a structure inspired by Keep a Changelog and semantic version
11
17
  - Completed HU 2.1 (`Fast path de estaticos en Node`).
12
18
  - Optimized static serving in `framework/router.js`:
13
19
  - Added warm-path static resolution cache (`route + accept-encoding`) to avoid repeated candidate resolution work.
14
- - Added bounded metadata revalidation window for conditional requests to reduce repeated `fs.stat` pressure.
20
+ - Keeps strict conditional metadata revalidation so content changes are visible on reload without stale `304`.
15
21
  - Consolidated static header assembly and reused mime header maps.
16
22
  - Kept stream-based delivery for large assets and tuned `createReadStream` chunk size.
23
+ - Added asset URL versioning in `framework/dipper.js` (`?v=size-mtime`) for local scripts/styles.
24
+ - Updated `gulp dev` watch flow so JS/CSS recompiles also trigger template compilation and refresh asset URLs in HTML.
17
25
  - Added reproducible local benchmark:
18
26
  - New script: `npm run benchmark:static`.
19
27
  - New guide: `docs/benchmark-static.md`.
@@ -103,4 +111,5 @@ The format follows a structure inspired by Keep a Changelog and semantic version
103
111
  [1.3.4]: https://github.com/nalancer08/VanillaJet/releases/tag/v1.3.4
104
112
  [1.3.5]: https://github.com/nalancer08/VanillaJet/releases/tag/v1.3.5
105
113
  [1.3.6]: https://github.com/nalancer08/VanillaJet/releases/tag/v1.3.6
114
+ [1.4.2]: https://github.com/nalancer08/VanillaJet/releases/tag/v1.4.2
106
115
  [1.4.1]: https://github.com/nalancer08/VanillaJet/releases/tag/v1.4.1
package/README.md CHANGED
@@ -6,7 +6,7 @@ Node.js framework for building SPA applications with a JS/CSS/HTML build pipelin
6
6
 
7
7
  ## Current version
8
8
 
9
- - Version: `1.4.1`
9
+ - Version: `1.4.2`
10
10
  - Changelog: see [`CHANGELOG.md`](./CHANGELOG.md)
11
11
  - Improvement plan (performance and backward compatibility): see `ROADMAP_INTEGRAL.md`
12
12
 
@@ -125,7 +125,7 @@ Behavior details:
125
125
  Static serving includes a warm-path optimization focused on Node runtime latency:
126
126
 
127
127
  - Reuses static resolution for repeated requests (`route + accept-encoding`).
128
- - Reduces repeated metadata refresh with bounded revalidation windows.
128
+ - Keeps conditional revalidation (`ETag`/`Last-Modified`) strict so reload reflects changes immediately.
129
129
  - Keeps streaming strategy for large assets (`fs.createReadStream`) with tuned chunk size.
130
130
  - Preserves conditional cache behavior (`ETag`/`Last-Modified` + `304`) and precompressed fallback contract.
131
131
 
@@ -117,7 +117,7 @@ Cada historia incluye su ciclo completo: fases, tareas, entregables, metricas, c
117
117
 
118
118
  ## EPIC 2 - Performance Node + DX de compilacion (foco actual)
119
119
 
120
- ### HU 2.1 - Fast path de estaticos en Node (completada `v1.4.1`)
120
+ ### HU 2.1 - Fast path de estaticos en Node (completada `v1.4.2`)
121
121
 
122
122
  #### Fases
123
123
  - F1: profiling de request estatico.
@@ -93,12 +93,12 @@ Dipper.prototype.img = function(filename) {
93
93
 
94
94
  Dipper.prototype.script = function(filename) {
95
95
  let dir = this.getDir('scripts', false);
96
- return this.urlTo(dir + filename);
96
+ return this.versionedUrl(dir + filename);
97
97
  }
98
98
 
99
99
  Dipper.prototype.style = function(filename) {
100
100
  let dir = this.getDir('styles', false);
101
- return this.urlTo(dir + filename);;
101
+ return this.versionedUrl(dir + filename);;
102
102
  }
103
103
 
104
104
  Dipper.prototype.pdf = function(filename) {
@@ -128,6 +128,36 @@ Dipper.prototype.urlTo = function (route) {
128
128
  return '/' + route.replace(/^\/+/, '');
129
129
  };
130
130
 
131
+ Dipper.prototype.versionedUrl = function(route) {
132
+ const fs = require('fs');
133
+ const path = require('path');
134
+ const normalizedUrl = this.urlTo(route);
135
+
136
+ // External URLs should remain untouched.
137
+ if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(normalizedUrl)) {
138
+ return normalizedUrl;
139
+ }
140
+
141
+ try {
142
+ const filePath = path.join(this.processCwd(), normalizedUrl.replace(/^\//, ''));
143
+ const stats = fs.statSync(filePath);
144
+ if (!stats.isFile()) {
145
+ return normalizedUrl;
146
+ }
147
+
148
+ const version = `${stats.size}-${Math.floor(stats.mtimeMs)}`;
149
+ return this.appendQueryParam(normalizedUrl, 'v', version);
150
+ } catch (err) {
151
+ // If file does not exist yet, keep legacy behavior.
152
+ return normalizedUrl;
153
+ }
154
+ }
155
+
156
+ Dipper.prototype.appendQueryParam = function(url, key, value) {
157
+ const separator = url.includes('?') ? '&' : '?';
158
+ return `${url}${separator}${key}=${encodeURIComponent(value)}`;
159
+ }
160
+
131
161
  Dipper.prototype.registerStyle = function(
132
162
  name, url, requires,
133
163
  cdn = false, async = false,
@@ -20,7 +20,6 @@ class Router {
20
20
  this.staticMetadataCache = new Map();
21
21
  this.staticResolutionCache = new Map();
22
22
  this.staticFileWatchers = new Map();
23
- this.staticMetadataMaxAgeMs = 1000;
24
23
  this.staticStreamChunkSize = 128 * 1024;
25
24
  this.mimes = {
26
25
  'png': 'image/png',
@@ -170,10 +169,6 @@ class Router {
170
169
  return callback(null, cachedMetadata);
171
170
  }
172
171
 
173
- if (cachedMetadata && forceRefresh && !obj.shouldRefreshConditionalMetadata(cachedMetadata)) {
174
- return callback(null, cachedMetadata);
175
- }
176
-
177
172
  fs.stat(filename, (err, stats) => {
178
173
  if (err || !stats.isFile()) {
179
174
  return callback(err || new Error('File not found'));
@@ -182,8 +177,7 @@ class Router {
182
177
  let metadata = {
183
178
  size: stats.size,
184
179
  lastModified: stats.mtime.toUTCString(),
185
- etag: `W/"${stats.size}-${Math.floor(stats.mtimeMs)}"`,
186
- cachedAt: Date.now()
180
+ etag: `W/"${stats.size}-${Math.floor(stats.mtimeMs)}"`
187
181
  };
188
182
 
189
183
  obj.staticMetadataCache.set(filename, metadata);
@@ -268,13 +262,6 @@ class Router {
268
262
  return `${route}|${normalizedEncodings}`;
269
263
  }
270
264
 
271
- shouldRefreshConditionalMetadata(metadata) {
272
- if (!metadata || !metadata.cachedAt) {
273
- return true;
274
- }
275
- return Date.now() - metadata.cachedAt > this.staticMetadataMaxAgeMs;
276
- }
277
-
278
265
  buildStaticHeaders(extHeader, candidates, contentEncoding, metadata) {
279
266
  let staticHeaders = Object.assign({}, extHeader);
280
267
  if (contentEncoding) {
package/gulpfile.js CHANGED
@@ -123,7 +123,8 @@ function watchFiles(cb) {
123
123
  // Watch LESS files
124
124
  watch([`${base}/assets/styles/less/**/*.less`], gulp.series(
125
125
  buildLess,
126
- compressCss
126
+ compressCss,
127
+ compileTemplates
127
128
  ));
128
129
 
129
130
  // Watch HTML files
@@ -138,7 +139,8 @@ function watchFiles(cb) {
138
139
  uglifyJs,
139
140
  concatJs,
140
141
  cleanMinified,
141
- compressJs
142
+ compressJs,
143
+ compileTemplates
142
144
  ));
143
145
 
144
146
  cb();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-jet",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "VannilaJet framework",
5
5
  "main": "index.js",
6
6
  "bin": {