@zambon-dev/framework 1.3.0 → 1.3.1
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
|
@@ -23,6 +23,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
23
23
|
|
|
24
24
|
### ⚠ Breaking Changes / Migration
|
|
25
25
|
|
|
26
|
+
## [1.3.1] - 2026-09-10
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **Returning to an already-open tab no longer crashes with `Maximum call stack size exceeded`.**
|
|
31
|
+
It affected any screen whose route nests two empty-path levels — the shape every list screen uses:
|
|
32
|
+
`path: ''` with `DefaultTabViewComponent`, and a child `path: ''` with the list component.
|
|
33
|
+
|
|
34
|
+
`CustomReuseStrategy` keyed its detached-view cache on `route.component.name`, and a production
|
|
35
|
+
build renames every class: esbuild wraps each component as `X = (() => { class i { } return i; })()`,
|
|
36
|
+
so the name is one mangled letter that the whole chunk shares. An empty path contributes no URL
|
|
37
|
+
segment, so both levels also resolved to the same URL — which left the two cache keys identical.
|
|
38
|
+
Storing one handle overwrote the other, both route levels were then handed the same detached view,
|
|
39
|
+
and Angular blew the stack building a router state whose node was its own descendant.
|
|
40
|
+
|
|
41
|
+
The key is now built from the component's **identity** and the route's depth rather than its name.
|
|
42
|
+
Applications need to change nothing. Worth knowing when reading old reports of this crash: it only
|
|
43
|
+
ever reproduced in a minified build — `ng serve` keeps real class names — and only on the screens
|
|
44
|
+
whose chunk happened to mangle to the same letter as the framework's, so a rebuild could move the
|
|
45
|
+
symptom from one screen to another.
|
|
46
|
+
|
|
26
47
|
## [1.3.0] - 2026-07-30
|
|
27
48
|
|
|
28
49
|
### Added
|
|
@@ -121,7 +142,8 @@ None.
|
|
|
121
142
|
available via [GitHub Releases](https://github.com/RicardoZambon/ZLibraries/releases) and the
|
|
122
143
|
`framework-v*` tags.
|
|
123
144
|
|
|
124
|
-
[Unreleased]: https://github.com/RicardoZambon/ZLibraries/compare/framework-v1.3.
|
|
145
|
+
[Unreleased]: https://github.com/RicardoZambon/ZLibraries/compare/framework-v1.3.1...HEAD
|
|
146
|
+
[1.3.1]: https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.3.1
|
|
125
147
|
[1.3.0]: https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.3.0
|
|
126
148
|
[1.2.1]: https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.2.1
|
|
127
149
|
[1.2.0]: https://github.com/RicardoZambon/ZLibraries/releases/tag/framework-v1.2.0
|
|
@@ -121,6 +121,8 @@ let CustomReuseStrategy = class CustomReuseStrategy {
|
|
|
121
121
|
tabService;
|
|
122
122
|
applicationRef = inject(ApplicationRef);
|
|
123
123
|
cachedHandles = {};
|
|
124
|
+
componentIDs = new WeakMap();
|
|
125
|
+
nextComponentID = 1;
|
|
124
126
|
//#endregion
|
|
125
127
|
//#region Properties
|
|
126
128
|
//#endregion
|
|
@@ -193,8 +195,39 @@ let CustomReuseStrategy = class CustomReuseStrategy {
|
|
|
193
195
|
if (this.clones[url]) {
|
|
194
196
|
url = this.clones[url];
|
|
195
197
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
+
// The component is identified by identity, never by name. A production build renames every
|
|
199
|
+
// class: esbuild wraps each component as `X = (() => { class i { } return i; })()`, so
|
|
200
|
+
// `component.name` is a single mangled letter that the whole chunk shares. Nested empty-path
|
|
201
|
+
// routes already resolve to the same URL, because an empty path contributes no segment, so a
|
|
202
|
+
// key built from the name collapsed a screen's tab view and its list into one entry: storing
|
|
203
|
+
// one overwrote the other, both levels were then handed the same detached view, and Angular
|
|
204
|
+
// blew the stack building a router state whose node was its own descendant. That reproduced
|
|
205
|
+
// only in a minified build, and only on the screens whose chunk happened to mangle to the
|
|
206
|
+
// same letter as the framework's.
|
|
207
|
+
//
|
|
208
|
+
// The depth is part of the key too, so two sibling routes sharing a component type under one
|
|
209
|
+
// URL cannot collide either.
|
|
210
|
+
return `${url}-${this.getRouteDepth(route)}-${this.getComponentID(route.component)}`;
|
|
211
|
+
}
|
|
212
|
+
getComponentID(component) {
|
|
213
|
+
if (typeof component !== 'function') {
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
let componentID = this.componentIDs.get(component);
|
|
217
|
+
if (componentID === undefined) {
|
|
218
|
+
componentID = this.nextComponentID++;
|
|
219
|
+
this.componentIDs.set(component, componentID);
|
|
220
|
+
}
|
|
221
|
+
return componentID;
|
|
222
|
+
}
|
|
223
|
+
getRouteDepth(route) {
|
|
224
|
+
let depth = 0;
|
|
225
|
+
let current = route.parent;
|
|
226
|
+
while (current) {
|
|
227
|
+
depth++;
|
|
228
|
+
current = current.parent;
|
|
229
|
+
}
|
|
230
|
+
return depth;
|
|
198
231
|
}
|
|
199
232
|
getUrlFromRoute(route) {
|
|
200
233
|
const segments = [];
|