fusee-framework 1.6.0 → 1.8.0
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/README.md +145 -123
- package/app/benchmark/benchmark.js +633 -0
- package/app/benchmark/index.html +102 -0
- package/app/benchmark/styles.css +249 -0
- package/app/components/Counter.js +17 -7
- package/app/components/CounterSection.js +3 -4
- package/app/main.js +50 -62
- package/app/main.ts +78 -63
- package/app/pages/404.js +16 -0
- package/app/pages/[...all].js +35 -0
- package/app/pages/[...all].ts +35 -0
- package/app/pages/_layout.js +53 -0
- package/app/pages/_layout.ts +26 -0
- package/app/pages/about.js +23 -0
- package/app/pages/about.ts +29 -0
- package/app/pages/async-work.js +65 -0
- package/app/pages/composables.js +158 -0
- package/app/pages/composables.ts +158 -0
- package/app/pages/dynamic.js +40 -0
- package/app/pages/dynamic.ts +40 -0
- package/app/pages/index.js +137 -0
- package/app/pages/index.ts +87 -0
- package/app/pages/login.ts +21 -0
- package/app/pages/objects/[id].js +56 -0
- package/app/pages/objects/[id].ts +56 -0
- package/app/pages/posts/[category]/[slug].js +56 -0
- package/app/pages/posts/[category]/[slug].ts +56 -0
- package/app/pages/router-example.js +79 -0
- package/app/pages/router-example.ts +56 -0
- package/app/pages/scroll-demo.ts +102 -0
- package/app/pages/test-for.js +65 -0
- package/app/pages/test-for.ts +80 -0
- package/app/pages/users/[id].js +24 -0
- package/app/pages/users/[id].ts +46 -0
- package/app/pages/users/index.js +22 -0
- package/app/pages/users/index.ts +29 -0
- package/app/pages/users.js +43 -0
- package/app/pages/users.ts +78 -0
- package/app/providers/ChildComponent.js +8 -2
- package/app/providers/ChildComponent.ts +47 -47
- package/app/providers/GrandchildComponent.js +19 -0
- package/app/providers/ParentComponent.js +5 -1
- package/app/routes/About.js +1 -1
- package/app/routes/About.ts +1 -1
- package/app/routes/AuthRoutes.ts +41 -0
- package/app/routes/Home.js +9 -0
- package/app/routes/NotFound.js +16 -0
- package/app/routes/NotFound.ts +16 -0
- package/app/routes/RouterExample.js +78 -0
- package/app/routes/RouterExample.ts +56 -0
- package/app/routes/TestResource.js +47 -0
- package/app/routes/WildcardDemo.js +35 -0
- package/app/routes/WildcardDemo.ts +35 -0
- package/app/services.js +15 -0
- package/auto-imports.d.ts +9 -0
- package/bin/create-fusee-linux +0 -0
- package/bin/create-fusee-mac-arm64 +0 -0
- package/bin/create-fusee-mac-intel +0 -0
- package/bin/create-fusee-win.exe +0 -0
- package/bin/create-fusee.exe +0 -0
- package/bin/index.js +38 -0
- package/create-fusee-go/cmd/generate.go +72 -0
- package/create-fusee-go/cmd/init.go +38 -0
- package/create-fusee-go/cmd/root.go +121 -0
- package/create-fusee-go/go.mod +10 -0
- package/create-fusee-go/go.sum +10 -0
- package/create-fusee-go/internal/assets/assets.go +78 -0
- package/create-fusee-go/internal/assets/embed/framework/auto-import-preset.js +9 -0
- package/create-fusee-go/internal/assets/embed/framework/core/compiler.js +253 -0
- package/create-fusee-go/internal/assets/embed/framework/core/component.js +266 -0
- package/create-fusee-go/internal/assets/embed/framework/core/composable.js +27 -0
- package/create-fusee-go/internal/assets/embed/framework/core/directives.js +875 -0
- package/create-fusee-go/internal/assets/embed/framework/core/evaluator.js +153 -0
- package/create-fusee-go/internal/assets/embed/framework/core/event-delegation.js +191 -0
- package/create-fusee-go/internal/assets/embed/framework/core/signal.js +412 -0
- package/create-fusee-go/internal/assets/embed/framework/core/store.js +81 -0
- package/create-fusee-go/internal/assets/embed/framework/index.d.ts +3 -0
- package/create-fusee-go/internal/assets/embed/framework/index.js +10 -0
- package/create-fusee-go/internal/assets/embed/framework/router/file-router.js +177 -0
- package/create-fusee-go/internal/assets/embed/framework/router/router.js +462 -0
- package/create-fusee-go/internal/assets/embed/framework/types/compiler.d.ts +15 -0
- package/create-fusee-go/internal/assets/embed/framework/types/component.d.ts +88 -0
- package/create-fusee-go/internal/assets/embed/framework/types/composable.d.ts +14 -0
- package/create-fusee-go/internal/assets/embed/framework/types/directives.d.ts +55 -0
- package/create-fusee-go/internal/assets/embed/framework/types/evaluator.d.ts +13 -0
- package/create-fusee-go/internal/assets/embed/framework/types/event-delegation.d.ts +32 -0
- package/create-fusee-go/internal/assets/embed/framework/types/index.d.ts +22 -0
- package/create-fusee-go/internal/assets/embed/framework/types/router.d.ts +32 -0
- package/create-fusee-go/internal/assets/embed/framework/types/signal.d.ts +79 -0
- package/create-fusee-go/internal/assets/embed/framework/types/store.d.ts +8 -0
- package/create-fusee-go/internal/assets/templates/Counter.tmpl +26 -0
- package/create-fusee-go/internal/assets/templates/component.tmpl +11 -0
- package/create-fusee-go/internal/assets/templates/components/Loading.tmpl +24 -0
- package/create-fusee-go/internal/assets/templates/index.html.tmpl +15 -0
- package/create-fusee-go/internal/assets/templates/main.tmpl +14 -0
- package/create-fusee-go/internal/assets/templates/package.json.tmpl +14 -0
- package/create-fusee-go/internal/assets/templates/page.tmpl +12 -0
- package/create-fusee-go/internal/assets/templates/pages/about.tmpl +20 -0
- package/create-fusee-go/internal/assets/templates/pages/index.tmpl +22 -0
- package/create-fusee-go/internal/assets/templates/pages/layout.tmpl +18 -0
- package/create-fusee-go/internal/assets/templates/tsconfig.json.tmpl +14 -0
- package/create-fusee-go/internal/assets/templates/vite.config.js.tmpl +18 -0
- package/create-fusee-go/main.go +7 -0
- package/framework/auto-import-preset.js +13 -9
- package/framework/core/compiler.js +252 -249
- package/framework/core/component.js +279 -260
- package/framework/core/di.js +187 -0
- package/framework/core/directives.js +819 -786
- package/framework/core/evaluator.js +153 -70
- package/framework/core/event-delegation.js +33 -14
- package/framework/core/reconcile.js +151 -0
- package/framework/core/signal.js +679 -403
- package/framework/core/store.js +322 -81
- package/framework/index.js +12 -11
- package/framework/router/file-router.js +177 -0
- package/framework/router/router.js +554 -80
- package/framework/types/component.d.ts +89 -88
- package/framework/types/di.d.ts +43 -0
- package/framework/types/event-delegation.d.ts +0 -58
- package/framework/types/index.d.ts +32 -22
- package/framework/types/reconcile.d.ts +7 -0
- package/framework/types/router.d.ts +42 -15
- package/framework/types/signal.d.ts +147 -78
- package/framework/types/store.d.ts +66 -8
- package/index.html +638 -626
- package/package.json +5 -3
- package/tsconfig.json +1 -0
- package/vite.config.js +7 -1
- /package/{framework → create-fusee-go/internal/assets/embed/framework}/bin/create-fusee.js +0 -0
|
@@ -1,70 +1,153 @@
|
|
|
1
|
-
export const MUSTACHE_RE = /\{\{\s*(.+?)\s*\}\}/
|
|
2
|
-
|
|
3
|
-
const SENSITIVE_ATTRS = ['href', 'src', 'srcset', 'formaction', 'xlink:href', 'data']
|
|
4
|
-
const DANGEROUS_SCHEMES = /^(javascript|data|vbscript|file):/i
|
|
5
|
-
|
|
6
|
-
export function evaluateExpression(expr, context, extraContext = {}, unwrapSignals = true) {
|
|
7
|
-
const keys = []
|
|
8
|
-
const values = []
|
|
9
|
-
|
|
10
|
-
const exprWithoutStrings = expr.replace(/'[^']*'|"[^"]*"/g, ' ')
|
|
11
|
-
const identifiers = new Set(exprWithoutStrings.match(/[a-zA-Z_$][a-zA-Z0-9_$]*/g) || [])
|
|
12
|
-
|
|
13
|
-
for (const k in context) {
|
|
14
|
-
if (identifiers.has(k)) {
|
|
15
|
-
keys.push(k)
|
|
16
|
-
const val = context[k]
|
|
17
|
-
values.push(
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
for (const k in extraContext) {
|
|
22
|
-
if (!keys.includes(k)) {
|
|
23
|
-
keys.push(k)
|
|
24
|
-
values.push(extraContext[k])
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const isMultiStatement = expr.includes(';') || expr.includes('console.')
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
1
|
+
export const MUSTACHE_RE = /\{\{\s*(.+?)\s*\}\}/
|
|
2
|
+
|
|
3
|
+
const SENSITIVE_ATTRS = ['href', 'src', 'srcset', 'formaction', 'xlink:href', 'data']
|
|
4
|
+
const DANGEROUS_SCHEMES = /^(javascript|data|vbscript|file):/i
|
|
5
|
+
|
|
6
|
+
export function evaluateExpression(expr, context, extraContext = {}, unwrapSignals = true) {
|
|
7
|
+
const keys = []
|
|
8
|
+
const values = []
|
|
9
|
+
|
|
10
|
+
const exprWithoutStrings = expr.replace(/'[^']*'|"[^"]*"/g, ' ')
|
|
11
|
+
const identifiers = new Set(exprWithoutStrings.match(/[a-zA-Z_$][a-zA-Z0-9_$]*/g) || [])
|
|
12
|
+
|
|
13
|
+
for (const k in context) {
|
|
14
|
+
if (identifiers.has(k)) {
|
|
15
|
+
keys.push(k)
|
|
16
|
+
const val = context[k]
|
|
17
|
+
values.push(val)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const k in extraContext) {
|
|
22
|
+
if (!keys.includes(k)) {
|
|
23
|
+
keys.push(k)
|
|
24
|
+
values.push(extraContext[k])
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const isMultiStatement = expr.includes(';') || expr.includes('console.')
|
|
30
|
+
let body
|
|
31
|
+
if (isMultiStatement) {
|
|
32
|
+
body = expr
|
|
33
|
+
} else {
|
|
34
|
+
const keysToUnwrap = unwrapSignals ? keys : []
|
|
35
|
+
const unwrapStatements = keysToUnwrap.map(id =>
|
|
36
|
+
`const __${id} = typeof ${id} === 'function' && ${id}.isSignal ? ${id}() : ${id};`
|
|
37
|
+
).join('\n')
|
|
38
|
+
let wrappedExpr = expr
|
|
39
|
+
for (const id of keysToUnwrap) {
|
|
40
|
+
wrappedExpr = wrappedExpr.replace(new RegExp('\\b' + id + '\\b(?![\\.\\(])', 'g'), '__' + id)
|
|
41
|
+
}
|
|
42
|
+
body = (unwrapStatements ? unwrapStatements + '\n' : '') + `return ${wrappedExpr}`
|
|
43
|
+
}
|
|
44
|
+
const fn = new Function(...keys, body)
|
|
45
|
+
return fn(...values)
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.warn(`[framework] Error evaluating "${expr}":`, e)
|
|
48
|
+
return ''
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function parseInterpolation(str) {
|
|
53
|
+
const parts = []
|
|
54
|
+
let i = 0
|
|
55
|
+
let lastIndex = 0
|
|
56
|
+
|
|
57
|
+
while (i < str.length) {
|
|
58
|
+
if (str[i] === '{' && str[i+1] === '{') {
|
|
59
|
+
if (i > 0 && str[i-1] === '\\') {
|
|
60
|
+
if (i - 1 > lastIndex) {
|
|
61
|
+
parts.push({ type: 'static', value: str.slice(lastIndex, i - 1) })
|
|
62
|
+
}
|
|
63
|
+
parts.push({ type: 'static', value: '{{' })
|
|
64
|
+
i += 2
|
|
65
|
+
lastIndex = i
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (i > lastIndex) {
|
|
70
|
+
parts.push({ type: 'static', value: str.slice(lastIndex, i) })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let start = i + 2
|
|
74
|
+
let j = start
|
|
75
|
+
let braceLevel = 0
|
|
76
|
+
let inString = false
|
|
77
|
+
let stringChar = null
|
|
78
|
+
|
|
79
|
+
while (j < str.length) {
|
|
80
|
+
const char = str[j]
|
|
81
|
+
|
|
82
|
+
if (inString) {
|
|
83
|
+
if (char === '\\') {
|
|
84
|
+
j += 2
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
if (char === stringChar) {
|
|
88
|
+
inString = false
|
|
89
|
+
}
|
|
90
|
+
j++
|
|
91
|
+
continue
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (char === "'" || char === '"' || char === '`') {
|
|
95
|
+
inString = true
|
|
96
|
+
stringChar = char
|
|
97
|
+
j++
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (char === '{') {
|
|
102
|
+
braceLevel++
|
|
103
|
+
j++
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (char === '}') {
|
|
108
|
+
if (braceLevel > 0) {
|
|
109
|
+
braceLevel--
|
|
110
|
+
j++
|
|
111
|
+
continue
|
|
112
|
+
} else if (j + 1 < str.length && str[j+1] === '}') {
|
|
113
|
+
const key = str.slice(start, j).trim()
|
|
114
|
+
parts.push({ type: 'dynamic', key })
|
|
115
|
+
i = j + 2
|
|
116
|
+
lastIndex = i
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
j++
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (j >= str.length) {
|
|
125
|
+
parts.push({ type: 'static', value: '{{' })
|
|
126
|
+
i += 2
|
|
127
|
+
lastIndex = i
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
} else {
|
|
132
|
+
i++
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (lastIndex < str.length) {
|
|
137
|
+
parts.push({ type: 'static', value: str.slice(lastIndex) })
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return parts
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function sanitizeAttr(name, value) {
|
|
144
|
+
const attrName = name.toLowerCase()
|
|
145
|
+
if (SENSITIVE_ATTRS.includes(attrName)) {
|
|
146
|
+
const trimmed = value.trim()
|
|
147
|
+
if (DANGEROUS_SCHEMES.test(trimmed)) {
|
|
148
|
+
console.warn(`[framework] Blocked potential XSS on ${name}: "${value}"`)
|
|
149
|
+
return 'about:blank'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return value
|
|
153
|
+
}
|
|
@@ -58,7 +58,7 @@ function ensureDocumentListener(eventType) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export function registerDelegatedEvent(element, eventType, handler, options = {}) {
|
|
61
|
-
const { modifiers = [], context, expr } = options
|
|
61
|
+
const { modifiers = [], context, expr, handlerState } = options
|
|
62
62
|
const normalizedEvent = eventType.toLowerCase()
|
|
63
63
|
|
|
64
64
|
ensureDocumentListener(normalizedEvent)
|
|
@@ -72,12 +72,23 @@ export function registerDelegatedEvent(element, eventType, handler, options = {}
|
|
|
72
72
|
modifiers,
|
|
73
73
|
context,
|
|
74
74
|
expr,
|
|
75
|
-
cleanup: null
|
|
75
|
+
cleanup: null,
|
|
76
|
+
handlerState: handlerState || { timeoutId: null, throttleTimeoutId: null, lastRun: 0 }
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
delegatedListeners.get(normalizedEvent).add(entry)
|
|
79
80
|
|
|
80
81
|
return function cleanup() {
|
|
82
|
+
const state = entry.handlerState
|
|
83
|
+
if (state.timeoutId) {
|
|
84
|
+
clearTimeout(state.timeoutId)
|
|
85
|
+
state.timeoutId = null
|
|
86
|
+
}
|
|
87
|
+
if (state.throttleTimeoutId) {
|
|
88
|
+
clearTimeout(state.throttleTimeoutId)
|
|
89
|
+
state.throttleTimeoutId = null
|
|
90
|
+
}
|
|
91
|
+
|
|
81
92
|
const listeners = delegatedListeners.get(normalizedEvent)
|
|
82
93
|
if (listeners) {
|
|
83
94
|
listeners.delete(entry)
|
|
@@ -93,10 +104,10 @@ export function registerDelegatedEvent(element, eventType, handler, options = {}
|
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
|
|
96
|
-
export function createEventHandler(handler, modifiers, context, expr) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
export function createEventHandler(handler, modifiers, context, expr, state = null) {
|
|
108
|
+
const timeoutId = { value: null }
|
|
109
|
+
const throttleTimeoutId = { value: null }
|
|
110
|
+
const lastRun = { value: 0 }
|
|
100
111
|
|
|
101
112
|
let debounceMs = 0
|
|
102
113
|
let throttleMs = 0
|
|
@@ -146,25 +157,33 @@ export function createEventHandler(handler, modifiers, context, expr) {
|
|
|
146
157
|
|
|
147
158
|
if (throttleMs > 0) {
|
|
148
159
|
const now = Date.now()
|
|
149
|
-
const remaining = throttleMs - (now - lastRun)
|
|
160
|
+
const remaining = throttleMs - (now - lastRun.value)
|
|
150
161
|
|
|
151
162
|
if (remaining <= 0) {
|
|
152
|
-
clearTimeout(throttleTimeoutId)
|
|
153
|
-
|
|
163
|
+
if (state && state.throttleTimeoutId) clearTimeout(state.throttleTimeoutId)
|
|
164
|
+
else clearTimeout(throttleTimeoutId.value)
|
|
165
|
+
lastRun.value = now
|
|
154
166
|
executeLogic(e)
|
|
155
167
|
} else {
|
|
156
|
-
clearTimeout(throttleTimeoutId)
|
|
157
|
-
|
|
158
|
-
|
|
168
|
+
if (state && state.throttleTimeoutId) clearTimeout(state.throttleTimeoutId)
|
|
169
|
+
else clearTimeout(throttleTimeoutId.value)
|
|
170
|
+
const newTimeoutId = setTimeout(() => {
|
|
171
|
+
lastRun.value = Date.now()
|
|
159
172
|
executeLogic(e)
|
|
160
173
|
}, remaining)
|
|
174
|
+
if (state) state.throttleTimeoutId = newTimeoutId
|
|
175
|
+
else throttleTimeoutId.value = newTimeoutId
|
|
161
176
|
}
|
|
177
|
+
if (state) state.lastRun = lastRun.value
|
|
162
178
|
return
|
|
163
179
|
}
|
|
164
180
|
|
|
165
181
|
if (debounceMs > 0) {
|
|
166
|
-
clearTimeout(timeoutId)
|
|
167
|
-
|
|
182
|
+
if (state && state.timeoutId) clearTimeout(state.timeoutId)
|
|
183
|
+
else clearTimeout(timeoutId.value)
|
|
184
|
+
const newTimeoutId = setTimeout(() => executeLogic(e), debounceMs)
|
|
185
|
+
if (state) state.timeoutId = newTimeoutId
|
|
186
|
+
else timeoutId.value = newTimeoutId
|
|
168
187
|
} else {
|
|
169
188
|
executeLogic(e)
|
|
170
189
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
export function reconcileArrays(parentNode, anchor, oldList, newList, keyFn, createFn, updateFn) {
|
|
2
|
+
let oldEnd = oldList.length - 1;
|
|
3
|
+
let newEnd = newList.length - 1;
|
|
4
|
+
let oldStart = 0;
|
|
5
|
+
let newStart = 0;
|
|
6
|
+
|
|
7
|
+
let oldStartNode = oldList[oldStart];
|
|
8
|
+
let oldEndNode = oldList[oldEnd];
|
|
9
|
+
let newStartItem = newList[newStart];
|
|
10
|
+
let newEndItem = newList[newEnd];
|
|
11
|
+
|
|
12
|
+
const nextOrder = new Array(newList.length);
|
|
13
|
+
|
|
14
|
+
while (oldStart <= oldEnd && newStart <= newEnd) {
|
|
15
|
+
if (oldStartNode === null) {
|
|
16
|
+
oldStartNode = oldList[++oldStart];
|
|
17
|
+
} else if (oldEndNode === null) {
|
|
18
|
+
oldEndNode = oldList[--oldEnd];
|
|
19
|
+
} else if (keyFn(oldStartNode) === keyFn(newStartItem)) {
|
|
20
|
+
updateFn(oldStartNode, newStartItem, newStart);
|
|
21
|
+
nextOrder[newStart] = oldStartNode;
|
|
22
|
+
oldStartNode = oldList[++oldStart];
|
|
23
|
+
newStartItem = newList[++newStart];
|
|
24
|
+
} else if (keyFn(oldEndNode) === keyFn(newEndItem)) {
|
|
25
|
+
updateFn(oldEndNode, newEndItem, newEnd);
|
|
26
|
+
nextOrder[newEnd] = oldEndNode;
|
|
27
|
+
oldEndNode = oldList[--oldEnd];
|
|
28
|
+
newEndItem = newList[--newEnd];
|
|
29
|
+
} else if (keyFn(oldStartNode) === keyFn(newEndItem)) {
|
|
30
|
+
updateFn(oldStartNode, newEndItem, newEnd);
|
|
31
|
+
const nextSibling = oldEndNode.node.nextSibling;
|
|
32
|
+
parentNode.insertBefore(oldStartNode.node, nextSibling === anchor ? anchor : nextSibling);
|
|
33
|
+
nextOrder[newEnd] = oldStartNode;
|
|
34
|
+
oldStartNode = oldList[++oldStart];
|
|
35
|
+
newEndItem = newList[--newEnd];
|
|
36
|
+
} else if (keyFn(oldEndNode) === keyFn(newStartItem)) {
|
|
37
|
+
updateFn(oldEndNode, newStartItem, newStart);
|
|
38
|
+
parentNode.insertBefore(oldEndNode.node, oldStartNode.node);
|
|
39
|
+
nextOrder[newStart] = oldEndNode;
|
|
40
|
+
oldEndNode = oldList[--oldEnd];
|
|
41
|
+
newStartItem = newList[++newStart];
|
|
42
|
+
} else {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (oldStart > oldEnd) {
|
|
48
|
+
for (; newStart <= newEnd; ++newStart) {
|
|
49
|
+
const nextPos = newStart + 1;
|
|
50
|
+
const refNode = nextPos < newList.length && nextOrder[nextPos] ? nextOrder[nextPos].node : (oldStart < oldList.length && oldList[oldStart] ? oldList[oldStart].node : anchor);
|
|
51
|
+
const newNode = createFn(newList[newStart], newStart);
|
|
52
|
+
parentNode.insertBefore(newNode.node, refNode);
|
|
53
|
+
nextOrder[newStart] = newNode;
|
|
54
|
+
}
|
|
55
|
+
} else if (newStart > newEnd) {
|
|
56
|
+
for (; oldStart <= oldEnd; ++oldStart) {
|
|
57
|
+
if (oldList[oldStart]) {
|
|
58
|
+
const node = oldList[oldStart];
|
|
59
|
+
node.node.remove();
|
|
60
|
+
if (node.effects) {
|
|
61
|
+
node.effects.forEach(cleanup => typeof cleanup === 'function' && cleanup());
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
const keyToIndex = new Map();
|
|
67
|
+
for (let i = newStart; i <= newEnd; ++i) {
|
|
68
|
+
keyToIndex.set(keyFn(newList[i]), i);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let nodesToMove = 0;
|
|
72
|
+
let lastIndex = 0;
|
|
73
|
+
const newIndexToOldIndexMap = new Array(newEnd - newStart + 1).fill(0);
|
|
74
|
+
|
|
75
|
+
for (let i = oldStart; i <= oldEnd; ++i) {
|
|
76
|
+
const oldNode = oldList[i];
|
|
77
|
+
if (!oldNode) continue;
|
|
78
|
+
|
|
79
|
+
const newIndex = keyToIndex.get(keyFn(oldNode));
|
|
80
|
+
if (newIndex === undefined) {
|
|
81
|
+
oldNode.node.remove();
|
|
82
|
+
if (oldNode.effects) {
|
|
83
|
+
oldNode.effects.forEach(cleanup => typeof cleanup === 'function' && cleanup());
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
updateFn(oldNode, newList[newIndex], newIndex);
|
|
87
|
+
newIndexToOldIndexMap[newIndex - newStart] = i + 1;
|
|
88
|
+
if (newIndex >= lastIndex) {
|
|
89
|
+
lastIndex = newIndex;
|
|
90
|
+
} else {
|
|
91
|
+
nodesToMove++;
|
|
92
|
+
}
|
|
93
|
+
nextOrder[newIndex] = oldNode;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (let i = newEnd - newStart; i >= 0; i--) {
|
|
98
|
+
const newIndex = i + newStart;
|
|
99
|
+
const nextPos = newIndex + 1;
|
|
100
|
+
const refNode = nextPos < nextOrder.length && nextOrder[nextPos] ? nextOrder[nextPos].node : anchor;
|
|
101
|
+
|
|
102
|
+
if (newIndexToOldIndexMap[i] === 0) {
|
|
103
|
+
const newNode = createFn(newList[newIndex], newIndex);
|
|
104
|
+
parentNode.insertBefore(newNode.node, refNode);
|
|
105
|
+
nextOrder[newIndex] = newNode;
|
|
106
|
+
} else if (nodesToMove > 0) {
|
|
107
|
+
let currentPosIndex = i;
|
|
108
|
+
const oldNode = nextOrder[newIndex];
|
|
109
|
+
if (oldNode.node.nextSibling !== refNode) {
|
|
110
|
+
parentNode.insertBefore(oldNode.node, refNode);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return nextOrder;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function reconcile(container, oldList, newList, keyFn, createFn) {
|
|
120
|
+
if (!container._renderedNodes) {
|
|
121
|
+
container._renderedNodes = [];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const mappedOldList = container._renderedNodes;
|
|
125
|
+
const mappedNewList = newList.map((item, index) => ({
|
|
126
|
+
item: item,
|
|
127
|
+
key: keyFn(item),
|
|
128
|
+
rawIndex: index
|
|
129
|
+
}));
|
|
130
|
+
|
|
131
|
+
const nextOrder = reconcileArrays(
|
|
132
|
+
container,
|
|
133
|
+
null,
|
|
134
|
+
mappedOldList,
|
|
135
|
+
mappedNewList,
|
|
136
|
+
(x) => x.key,
|
|
137
|
+
(newItem) => {
|
|
138
|
+
const domNode = createFn(newItem.item);
|
|
139
|
+
return {
|
|
140
|
+
node: domNode,
|
|
141
|
+
item: newItem.item,
|
|
142
|
+
key: newItem.key
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
(oldNode, newItem) => {
|
|
146
|
+
oldNode.item = newItem.item;
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
container._renderedNodes = nextOrder;
|
|
151
|
+
}
|