@bitstillery/mithril 3.9.1 → 3.9.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/api/router.ts +5 -5
- package/package.json +1 -1
- package/pathname/compileTemplate.ts +3 -2
- package/querystring/parse.ts +6 -5
- package/render/hyperscript.ts +7 -5
- package/render/render.ts +12 -10
- package/render/ssrState.ts +2 -2
- package/server/logger.ts +1 -1
- package/server/session.ts +1 -1
- package/signal.ts +2 -2
- package/state.ts +73 -11
- package/store.ts +5 -4
- package/util/ssr.ts +1 -1
package/api/router.ts
CHANGED
|
@@ -167,7 +167,7 @@ export default function router($window: any, mountRedraw: MountRedraw) {
|
|
|
167
167
|
function getRedirectPath(redirectObj: RedirectObject): string {
|
|
168
168
|
// First try our REDIRECT symbol
|
|
169
169
|
if (REDIRECT in redirectObj) {
|
|
170
|
-
return redirectObj[REDIRECT]
|
|
170
|
+
return redirectObj[REDIRECT]!
|
|
171
171
|
}
|
|
172
172
|
// Otherwise, check all symbol keys
|
|
173
173
|
const symbolKeys = Object.getOwnPropertySymbols(redirectObj)
|
|
@@ -215,9 +215,9 @@ export default function router($window: any, mountRedraw: MountRedraw) {
|
|
|
215
215
|
function loop(i: number) {
|
|
216
216
|
if (!compiled) return
|
|
217
217
|
for (; i < compiled.length; i++) {
|
|
218
|
-
if (compiled[i]
|
|
219
|
-
let payload = compiled[i]
|
|
220
|
-
const matchedRoute = compiled[i]
|
|
218
|
+
if (compiled[i]!.check(data)) {
|
|
219
|
+
let payload = compiled[i]!.component
|
|
220
|
+
const matchedRoute = compiled[i]!.route
|
|
221
221
|
const localComp = payload
|
|
222
222
|
// Store the RouteResolver if payload has both onmatch and render
|
|
223
223
|
// This allows us to preserve the resolver even after onmatch returns a component
|
|
@@ -237,7 +237,7 @@ export default function router($window: any, mountRedraw: MountRedraw) {
|
|
|
237
237
|
// Handle redirect objects: explicit redirect signal
|
|
238
238
|
if (isRedirect(comp)) {
|
|
239
239
|
// Extract redirect target path
|
|
240
|
-
const redirectPath = comp
|
|
240
|
+
const redirectPath = getRedirectPath(comp)
|
|
241
241
|
const resolve = routeSetResolve
|
|
242
242
|
routeSetResolve = null
|
|
243
243
|
// Trigger navigation to redirect target; resolve original Promise when redirect completes
|
package/package.json
CHANGED
|
@@ -38,14 +38,15 @@ export default function compileTemplate(template: string): CompiledTemplate {
|
|
|
38
38
|
// First, check the params. Usually, there isn't any, and it's just
|
|
39
39
|
// checking a static set.
|
|
40
40
|
for (let i = 0; i < templateKeys.length; i++) {
|
|
41
|
-
if (templateData.params[templateKeys[i]] !== data.params[templateKeys[i]]) return false
|
|
41
|
+
if (templateData.params[templateKeys[i]!] !== data.params[templateKeys[i]!]) return false
|
|
42
42
|
}
|
|
43
43
|
// If no interpolations exist, let's skip all the ceremony
|
|
44
44
|
if (!keys.length) return regexp.test(data.path)
|
|
45
45
|
const values = regexp.exec(data.path)
|
|
46
46
|
if (values == null) return false
|
|
47
47
|
for (let i = 0; i < keys.length; i++) {
|
|
48
|
-
|
|
48
|
+
// Every key is a non-optional capture group, so a match fills all of them.
|
|
49
|
+
data.params[keys[i]!.k] = keys[i]!.r ? values[i + 1] : decodeURIComponent(values[i + 1]!)
|
|
49
50
|
}
|
|
50
51
|
return true
|
|
51
52
|
}
|
package/querystring/parse.ts
CHANGED
|
@@ -8,9 +8,9 @@ export default function parseQueryString(string: string | null | undefined): Rec
|
|
|
8
8
|
const counters: Record<string, number> = {}
|
|
9
9
|
const data: Record<string, any> = {}
|
|
10
10
|
for (let i = 0; i < entries.length; i++) {
|
|
11
|
-
const entry = entries[i]
|
|
12
|
-
const key = decodeURIComponentSafe(entry[0])
|
|
13
|
-
let value: any = entry.length === 2 ? decodeURIComponentSafe(entry[1]) : ''
|
|
11
|
+
const entry = entries[i]!.split('=')
|
|
12
|
+
const key = decodeURIComponentSafe(entry[0]!)
|
|
13
|
+
let value: any = entry.length === 2 ? decodeURIComponentSafe(entry[1]!) : ''
|
|
14
14
|
|
|
15
15
|
if (value === 'true') value = true
|
|
16
16
|
else if (value === 'false') value = false
|
|
@@ -19,9 +19,10 @@ export default function parseQueryString(string: string | null | undefined): Rec
|
|
|
19
19
|
let cursor: any = data
|
|
20
20
|
if (key.indexOf('[') > -1) levels.pop()
|
|
21
21
|
for (let j = 0; j < levels.length; j++) {
|
|
22
|
-
const level = levels[j]
|
|
22
|
+
const level = levels[j]!
|
|
23
23
|
const nextLevel = levels[j + 1]
|
|
24
|
-
|
|
24
|
+
// Past the last level nextLevel is undefined, which parseInt reads as NaN.
|
|
25
|
+
const isNumber = nextLevel == '' || !isNaN(parseInt(nextLevel as string, 10))
|
|
25
26
|
let finalLevel: string | number
|
|
26
27
|
if (level === '') {
|
|
27
28
|
const key = levels.slice(0, j).join()
|
package/render/hyperscript.ts
CHANGED
|
@@ -31,25 +31,27 @@ function isFormAttributeKey(key: string): boolean {
|
|
|
31
31
|
return key === 'value' || key === 'checked' || key === 'selectedIndex' || key === 'selected'
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// A match fills groups 1-2 (tag/#id/.class) or groups 3-4 ([attr]), never both; group 6
|
|
35
|
+
// is absent for a bare `[attr]`, so a bare `[class]` joins in as an empty class.
|
|
34
36
|
function compileSelector(selector: string): {tag: string; attrs: Record<string, any>; is?: string} {
|
|
35
37
|
let match: RegExpExecArray | null
|
|
36
38
|
let tag = 'div'
|
|
37
|
-
const classes: string[] = []
|
|
39
|
+
const classes: (string | undefined)[] = []
|
|
38
40
|
let attrs: Record<string, any> = {}
|
|
39
41
|
let isStatic = true
|
|
40
42
|
while ((match = selectorParser.exec(selector)) !== null) {
|
|
41
43
|
const type = match[1]
|
|
42
44
|
const value = match[2]
|
|
43
|
-
if (type === '' && value !== '') tag = value
|
|
45
|
+
if (type === '' && value !== '') tag = value!
|
|
44
46
|
else if (type === '#') attrs.id = value
|
|
45
47
|
else if (type === '.') classes.push(value)
|
|
46
|
-
else if (match[3][0] === '[') {
|
|
48
|
+
else if (match[3]![0] === '[') {
|
|
47
49
|
let attrValue = match[6]
|
|
48
50
|
if (attrValue) attrValue = attrValue.replace(/\\(["'])/g, '$1').replace(/\\\\/g, '\\')
|
|
49
51
|
if (match[4] === 'class') classes.push(attrValue)
|
|
50
52
|
else {
|
|
51
|
-
attrs[match[4]] = attrValue === '' ? attrValue : attrValue || true
|
|
52
|
-
if (isFormAttributeKey(match[4])) isStatic = false
|
|
53
|
+
attrs[match[4]!] = attrValue === '' ? attrValue : attrValue || true
|
|
54
|
+
if (isFormAttributeKey(match[4]!)) isStatic = false
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
}
|
package/render/render.ts
CHANGED
|
@@ -895,16 +895,18 @@ export default function renderFactory() {
|
|
|
895
895
|
// of the items that are part of the longest increasing
|
|
896
896
|
// subsequence
|
|
897
897
|
const lisTemp: number[] = []
|
|
898
|
+
// Every index read below is in range: i < a.length, `result` is never empty and holds
|
|
899
|
+
// indices into `a`, and `lisTemp` mirrors `a`'s length.
|
|
898
900
|
function makeLisIndices(a: number[]): number[] {
|
|
899
901
|
const result = [0]
|
|
900
902
|
let u = 0,
|
|
901
903
|
v = 0
|
|
902
904
|
const il = (lisTemp.length = a.length)
|
|
903
|
-
for (let i = 0; i < il; i++) lisTemp[i] = a[i]
|
|
905
|
+
for (let i = 0; i < il; i++) lisTemp[i] = a[i]!
|
|
904
906
|
for (let i = 0; i < il; ++i) {
|
|
905
907
|
if (a[i] === -1) continue
|
|
906
|
-
const j = result[result.length - 1]
|
|
907
|
-
if (a[j] < a[i]) {
|
|
908
|
+
const j = result[result.length - 1]!
|
|
909
|
+
if (a[j]! < a[i]!) {
|
|
908
910
|
lisTemp[i] = j
|
|
909
911
|
result.push(i)
|
|
910
912
|
continue
|
|
@@ -915,22 +917,22 @@ export default function renderFactory() {
|
|
|
915
917
|
// Fast integer average without overflow.
|
|
916
918
|
|
|
917
919
|
const c = (u >>> 1) + (v >>> 1) + (u & v & 1)
|
|
918
|
-
if (a[result[c]] < a[i]) {
|
|
920
|
+
if (a[result[c]!]! < a[i]!) {
|
|
919
921
|
u = c + 1
|
|
920
922
|
} else {
|
|
921
923
|
v = c
|
|
922
924
|
}
|
|
923
925
|
}
|
|
924
|
-
if (a[i] < a[result[u]]) {
|
|
925
|
-
if (u > 0) lisTemp[i] = result[u - 1]
|
|
926
|
+
if (a[i]! < a[result[u]!]!) {
|
|
927
|
+
if (u > 0) lisTemp[i] = result[u - 1]!
|
|
926
928
|
result[u] = i
|
|
927
929
|
}
|
|
928
930
|
}
|
|
929
931
|
u = result.length
|
|
930
|
-
v = result[u - 1]
|
|
932
|
+
v = result[u - 1]!
|
|
931
933
|
while (u-- > 0) {
|
|
932
934
|
result[u] = v
|
|
933
|
-
v = lisTemp[v]
|
|
935
|
+
v = lisTemp[v]!
|
|
934
936
|
}
|
|
935
937
|
lisTemp.length = 0
|
|
936
938
|
return result
|
|
@@ -1453,14 +1455,14 @@ export default function renderFactory() {
|
|
|
1453
1455
|
false,
|
|
1454
1456
|
)
|
|
1455
1457
|
// Execute hooks from override render
|
|
1456
|
-
for (let i = 0; i < overrideHooks.length; i++) overrideHooks[i]()
|
|
1458
|
+
for (let i = 0; i < overrideHooks.length; i++) overrideHooks[i]!()
|
|
1457
1459
|
}
|
|
1458
1460
|
|
|
1459
1461
|
;(dom as any).vnodes = normalized
|
|
1460
1462
|
// `document.activeElement` can return null: https://html.spec.whatwg.org/multipage/interaction.html#dom-document-activeelement
|
|
1461
1463
|
if (active != null && activeElement(dom) !== active && typeof (active as any).focus === 'function')
|
|
1462
1464
|
(active as any).focus()
|
|
1463
|
-
for (let i = 0; i < hooks.length; i++) hooks[i]()
|
|
1465
|
+
for (let i = 0; i < hooks.length; i++) hooks[i]!()
|
|
1464
1466
|
} finally {
|
|
1465
1467
|
currentRedraw = prevRedraw
|
|
1466
1468
|
currentDOM = prevDOM
|
package/render/ssrState.ts
CHANGED
|
@@ -306,11 +306,11 @@ function restoreComputedProperties(state: State<any>, initial: any): void {
|
|
|
306
306
|
const keys = prefix ? prefix.split('.').filter((k) => k) : []
|
|
307
307
|
let targetState = target
|
|
308
308
|
for (let i = 0; i < keys.length; i++) {
|
|
309
|
-
if (!targetState[keys[i]]) {
|
|
309
|
+
if (!targetState[keys[i]!]) {
|
|
310
310
|
// Nested state doesn't exist yet, skip
|
|
311
311
|
return
|
|
312
312
|
}
|
|
313
|
-
targetState = targetState[keys[i]]
|
|
313
|
+
targetState = targetState[keys[i]!]
|
|
314
314
|
}
|
|
315
315
|
// Clear any existing signal in signalMap so function is re-initialized as ComputedSignal
|
|
316
316
|
if (targetState && typeof targetState === 'object' && (targetState as any).__isState) {
|
package/server/logger.ts
CHANGED
|
@@ -96,7 +96,7 @@ function getColorByHash(text: string): string {
|
|
|
96
96
|
colors.cyan,
|
|
97
97
|
]
|
|
98
98
|
const hash = hashString(text)
|
|
99
|
-
return colorOptions[hash % colorOptions.length]
|
|
99
|
+
return colorOptions[hash % colorOptions.length]!
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
const textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null
|
package/server/session.ts
CHANGED
|
@@ -83,5 +83,5 @@ export class MemorySessionStore implements SessionStore {
|
|
|
83
83
|
export function extractSessionId(req: Request): string | null {
|
|
84
84
|
const cookies = req.headers.get('cookie') || ''
|
|
85
85
|
const sessionIdMatch = cookies.match(/sessionId=([^;]+)/)
|
|
86
|
-
return sessionIdMatch ? sessionIdMatch[1] : null
|
|
86
|
+
return sessionIdMatch ? sessionIdMatch[1]! : null
|
|
87
87
|
}
|
package/signal.ts
CHANGED
|
@@ -188,7 +188,7 @@ export class ComputedSignal<T> extends Signal<T> {
|
|
|
188
188
|
this._compute = compute
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
get value(): T {
|
|
191
|
+
override get value(): T {
|
|
192
192
|
// Track access by other computed signals - this enables computed-to-computed dependency chains
|
|
193
193
|
// When computed B accesses computed A, A should notify B when A's dependencies change
|
|
194
194
|
if (currentEffect) {
|
|
@@ -259,7 +259,7 @@ export class ComputedSignal<T> extends Signal<T> {
|
|
|
259
259
|
this._markDirty()
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
set value(_newValue: T) {
|
|
262
|
+
override set value(_newValue: T) {
|
|
263
263
|
throw new Error('Computed signals are read-only')
|
|
264
264
|
}
|
|
265
265
|
}
|
package/state.ts
CHANGED
|
@@ -60,12 +60,37 @@ function isState(value: any): boolean {
|
|
|
60
60
|
return value && typeof value === 'object' && (value as any).__isState === true
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Built-ins whose methods need their internal slots. A proxy has none, and the get trap calls a
|
|
65
|
+
* function-valued property with the proxy as `this`, so a proxied Date or Map throws on every method
|
|
66
|
+
* call (and a Map's `get`/`set` read as a get/set descriptor). They are held as a signal's value
|
|
67
|
+
* instead, and replaced wholesale to notify.
|
|
68
|
+
*/
|
|
69
|
+
function isOpaqueObject(value: object): boolean {
|
|
70
|
+
return (
|
|
71
|
+
value instanceof Date ||
|
|
72
|
+
value instanceof Map ||
|
|
73
|
+
value instanceof Set ||
|
|
74
|
+
value instanceof WeakMap ||
|
|
75
|
+
value instanceof WeakSet ||
|
|
76
|
+
value instanceof RegExp ||
|
|
77
|
+
value instanceof Promise ||
|
|
78
|
+
value instanceof ArrayBuffer ||
|
|
79
|
+
ArrayBuffer.isView(value)
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
63
83
|
/**
|
|
64
84
|
* Check if a value is a get/set descriptor object (like JavaScript property descriptors)
|
|
65
85
|
* Used to detect computed properties defined as { get: () => T, set?: (value: T) => void }
|
|
66
86
|
*/
|
|
67
87
|
function isGetSetDescriptor(value: any): boolean {
|
|
68
|
-
return
|
|
88
|
+
return (
|
|
89
|
+
value &&
|
|
90
|
+
typeof value === 'object' &&
|
|
91
|
+
!isOpaqueObject(value) &&
|
|
92
|
+
(typeof value.get === 'function' || typeof value.set === 'function')
|
|
93
|
+
)
|
|
69
94
|
}
|
|
70
95
|
|
|
71
96
|
/**
|
|
@@ -203,7 +228,7 @@ export function state<T extends Record<string, any>>(initial: T, name?: string,
|
|
|
203
228
|
parentSignalMap?: Map<string, Signal<any> | ComputedSignal<any>>,
|
|
204
229
|
context?: InitContext,
|
|
205
230
|
): any {
|
|
206
|
-
if (obj === null || typeof obj !== 'object') {
|
|
231
|
+
if (obj === null || typeof obj !== 'object' || isOpaqueObject(obj)) {
|
|
207
232
|
return obj
|
|
208
233
|
}
|
|
209
234
|
|
|
@@ -863,6 +888,48 @@ export function state<T extends Record<string, any>>(initial: T, name?: string,
|
|
|
863
888
|
return wrapped
|
|
864
889
|
}
|
|
865
890
|
|
|
891
|
+
/** The built-ins `isOpaqueObject` keeps out of the proxy: state holds them as they are. */
|
|
892
|
+
type OpaqueObject =
|
|
893
|
+
| Date
|
|
894
|
+
| Map<any, any>
|
|
895
|
+
| Set<any>
|
|
896
|
+
| WeakMap<any, any>
|
|
897
|
+
| WeakSet<any>
|
|
898
|
+
| RegExp
|
|
899
|
+
| Promise<any>
|
|
900
|
+
| ArrayBuffer
|
|
901
|
+
| ArrayBufferView
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* A value as state hands it out: a function reads as its computed result, an object (or array) as its
|
|
905
|
+
* own State. Distributes over a union, so a nullable object (`Foo | null`) is still a State with its
|
|
906
|
+
* `$` signals once it is set.
|
|
907
|
+
*/
|
|
908
|
+
type StateValue<V> = V extends (...args: any[]) => infer R
|
|
909
|
+
? R
|
|
910
|
+
: V extends OpaqueObject
|
|
911
|
+
? V
|
|
912
|
+
: V extends Record<string, any>
|
|
913
|
+
? State<V>
|
|
914
|
+
: V
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* A property as state hands it out. Unlike an array element it doesn't distribute over a union, so a
|
|
918
|
+
* nullable object (`Foo | null`) stays its plain shape and a plain `Foo` can be written to it, as in 3.9.
|
|
919
|
+
* A `{get, set}` descriptor reads as its computed value.
|
|
920
|
+
*/
|
|
921
|
+
type StateProp<V> = [V] extends [never]
|
|
922
|
+
? V
|
|
923
|
+
: [V] extends [(...args: any[]) => infer R]
|
|
924
|
+
? R
|
|
925
|
+
: [V] extends [{get: () => infer R; set: (value: never) => void}]
|
|
926
|
+
? R
|
|
927
|
+
: [V] extends [OpaqueObject]
|
|
928
|
+
? V
|
|
929
|
+
: [V] extends [Record<string, any>]
|
|
930
|
+
? State<V>
|
|
931
|
+
: V
|
|
932
|
+
|
|
866
933
|
/**
|
|
867
934
|
* Mapped type that adds $prop for each key, returning the Signal for that property.
|
|
868
935
|
* - Primitives: $prop => Signal<T[K]>
|
|
@@ -874,24 +941,19 @@ export type StateSignals<T extends Record<string, any>> = {
|
|
|
874
941
|
// lookup by a `string` key would read as `Value | Signal<Value>`.
|
|
875
942
|
[K in keyof T as K extends string ? (string extends K ? never : `$${K}`) : never]: T[K] extends (...args: any[]) => infer R
|
|
876
943
|
? ComputedSignal<R>
|
|
877
|
-
: T[K]
|
|
878
|
-
? Signal<State<T[K]>>
|
|
879
|
-
: Signal<T[K]>
|
|
944
|
+
: Signal<StateProp<T[K]>>
|
|
880
945
|
}
|
|
881
|
-
type StateElem<E> = E extends Record<string, any> ? State<E> : E
|
|
882
946
|
|
|
883
|
-
export type StateArray<Elem> = Omit<Array<
|
|
947
|
+
export type StateArray<Elem> = Omit<Array<StateValue<Elem>>, 'fill' | 'push' | 'splice' | 'unshift'> & {
|
|
884
948
|
fill(value: Elem, start?: number, end?: number): StateArray<Elem>
|
|
885
949
|
push(...items: Elem[]): number
|
|
886
|
-
splice(start: number, deleteCount?: number, ...items: Elem[]):
|
|
950
|
+
splice(start: number, deleteCount?: number, ...items: Elem[]): StateValue<Elem>[]
|
|
887
951
|
unshift(...items: Elem[]): number
|
|
888
952
|
}
|
|
889
953
|
|
|
890
954
|
export type State<T extends Record<string, any>> = T extends (infer Elem)[]
|
|
891
955
|
? StateArray<Elem>
|
|
892
|
-
: {
|
|
893
|
-
[K in keyof T]: T[K] extends (...args: any[]) => infer R ? R : T[K] extends Record<string, any> ? State<T[K]> : T[K]
|
|
894
|
-
} & StateSignals<T>
|
|
956
|
+
: {[K in keyof T]: StateProp<T[K]>} & StateSignals<T>
|
|
895
957
|
|
|
896
958
|
/**
|
|
897
959
|
* A partial of a state shape at every depth, for the persistence tiers of a Store: each tier fills in
|
package/store.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {allowComputed, state,
|
|
1
|
+
import {allowComputed, state, updateStateRegistry} from './state'
|
|
2
|
+
import type {State, DeepPartial} from './state'
|
|
2
3
|
import {serializeStore, deserializeStore} from './render/ssrState'
|
|
3
4
|
|
|
4
5
|
// Helper function to restore computed properties (same as in ssrState.ts)
|
|
@@ -21,11 +22,11 @@ function restoreComputedProperties(state: State<any>, initial: any): void {
|
|
|
21
22
|
const keys = prefix ? prefix.split('.').filter((k) => k) : []
|
|
22
23
|
let targetState = target
|
|
23
24
|
for (let i = 0; i < keys.length; i++) {
|
|
24
|
-
if (!targetState || !targetState[keys[i]]) {
|
|
25
|
+
if (!targetState || !targetState[keys[i]!]) {
|
|
25
26
|
// Nested state doesn't exist yet, skip
|
|
26
27
|
return
|
|
27
28
|
}
|
|
28
|
-
targetState = targetState[keys[i]]
|
|
29
|
+
targetState = targetState[keys[i]!]
|
|
29
30
|
}
|
|
30
31
|
if (targetState) {
|
|
31
32
|
// Clear any existing signal in signalMap so function is re-initialized as ComputedSignal
|
|
@@ -536,7 +537,7 @@ export class Store<T extends Record<string, any> = Record<string, any>> {
|
|
|
536
537
|
try {
|
|
537
538
|
const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${key}=([^;]*)`))
|
|
538
539
|
if (!match) return {}
|
|
539
|
-
const parsed = JSON.parse(decodeURIComponent(match[1]))
|
|
540
|
+
const parsed = JSON.parse(decodeURIComponent(match[1]!))
|
|
540
541
|
return parsed && typeof parsed === 'object' ? parsed : {}
|
|
541
542
|
} catch {
|
|
542
543
|
return {}
|
package/util/ssr.ts
CHANGED
|
@@ -198,7 +198,7 @@ function formatCombinedStructure(parent: Element | Node | null, vnode: any, maxP
|
|
|
198
198
|
|
|
199
199
|
// Closing tags for DOM parents (in reverse order)
|
|
200
200
|
for (let i = domElements.length - 1; i >= 0; i--) {
|
|
201
|
-
lines.push(' '.repeat(i) + domElements[i]
|
|
201
|
+
lines.push(' '.repeat(i) + domElements[i]!.closeTag)
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
return lines.join('\n')
|