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.
Files changed (129) hide show
  1. package/README.md +145 -123
  2. package/app/benchmark/benchmark.js +633 -0
  3. package/app/benchmark/index.html +102 -0
  4. package/app/benchmark/styles.css +249 -0
  5. package/app/components/Counter.js +17 -7
  6. package/app/components/CounterSection.js +3 -4
  7. package/app/main.js +50 -62
  8. package/app/main.ts +78 -63
  9. package/app/pages/404.js +16 -0
  10. package/app/pages/[...all].js +35 -0
  11. package/app/pages/[...all].ts +35 -0
  12. package/app/pages/_layout.js +53 -0
  13. package/app/pages/_layout.ts +26 -0
  14. package/app/pages/about.js +23 -0
  15. package/app/pages/about.ts +29 -0
  16. package/app/pages/async-work.js +65 -0
  17. package/app/pages/composables.js +158 -0
  18. package/app/pages/composables.ts +158 -0
  19. package/app/pages/dynamic.js +40 -0
  20. package/app/pages/dynamic.ts +40 -0
  21. package/app/pages/index.js +137 -0
  22. package/app/pages/index.ts +87 -0
  23. package/app/pages/login.ts +21 -0
  24. package/app/pages/objects/[id].js +56 -0
  25. package/app/pages/objects/[id].ts +56 -0
  26. package/app/pages/posts/[category]/[slug].js +56 -0
  27. package/app/pages/posts/[category]/[slug].ts +56 -0
  28. package/app/pages/router-example.js +79 -0
  29. package/app/pages/router-example.ts +56 -0
  30. package/app/pages/scroll-demo.ts +102 -0
  31. package/app/pages/test-for.js +65 -0
  32. package/app/pages/test-for.ts +80 -0
  33. package/app/pages/users/[id].js +24 -0
  34. package/app/pages/users/[id].ts +46 -0
  35. package/app/pages/users/index.js +22 -0
  36. package/app/pages/users/index.ts +29 -0
  37. package/app/pages/users.js +43 -0
  38. package/app/pages/users.ts +78 -0
  39. package/app/providers/ChildComponent.js +8 -2
  40. package/app/providers/ChildComponent.ts +47 -47
  41. package/app/providers/GrandchildComponent.js +19 -0
  42. package/app/providers/ParentComponent.js +5 -1
  43. package/app/routes/About.js +1 -1
  44. package/app/routes/About.ts +1 -1
  45. package/app/routes/AuthRoutes.ts +41 -0
  46. package/app/routes/Home.js +9 -0
  47. package/app/routes/NotFound.js +16 -0
  48. package/app/routes/NotFound.ts +16 -0
  49. package/app/routes/RouterExample.js +78 -0
  50. package/app/routes/RouterExample.ts +56 -0
  51. package/app/routes/TestResource.js +47 -0
  52. package/app/routes/WildcardDemo.js +35 -0
  53. package/app/routes/WildcardDemo.ts +35 -0
  54. package/app/services.js +15 -0
  55. package/auto-imports.d.ts +9 -0
  56. package/bin/create-fusee-linux +0 -0
  57. package/bin/create-fusee-mac-arm64 +0 -0
  58. package/bin/create-fusee-mac-intel +0 -0
  59. package/bin/create-fusee-win.exe +0 -0
  60. package/bin/create-fusee.exe +0 -0
  61. package/bin/index.js +38 -0
  62. package/create-fusee-go/cmd/generate.go +72 -0
  63. package/create-fusee-go/cmd/init.go +38 -0
  64. package/create-fusee-go/cmd/root.go +121 -0
  65. package/create-fusee-go/go.mod +10 -0
  66. package/create-fusee-go/go.sum +10 -0
  67. package/create-fusee-go/internal/assets/assets.go +78 -0
  68. package/create-fusee-go/internal/assets/embed/framework/auto-import-preset.js +9 -0
  69. package/create-fusee-go/internal/assets/embed/framework/core/compiler.js +253 -0
  70. package/create-fusee-go/internal/assets/embed/framework/core/component.js +266 -0
  71. package/create-fusee-go/internal/assets/embed/framework/core/composable.js +27 -0
  72. package/create-fusee-go/internal/assets/embed/framework/core/directives.js +875 -0
  73. package/create-fusee-go/internal/assets/embed/framework/core/evaluator.js +153 -0
  74. package/create-fusee-go/internal/assets/embed/framework/core/event-delegation.js +191 -0
  75. package/create-fusee-go/internal/assets/embed/framework/core/signal.js +412 -0
  76. package/create-fusee-go/internal/assets/embed/framework/core/store.js +81 -0
  77. package/create-fusee-go/internal/assets/embed/framework/index.d.ts +3 -0
  78. package/create-fusee-go/internal/assets/embed/framework/index.js +10 -0
  79. package/create-fusee-go/internal/assets/embed/framework/router/file-router.js +177 -0
  80. package/create-fusee-go/internal/assets/embed/framework/router/router.js +462 -0
  81. package/create-fusee-go/internal/assets/embed/framework/types/compiler.d.ts +15 -0
  82. package/create-fusee-go/internal/assets/embed/framework/types/component.d.ts +88 -0
  83. package/create-fusee-go/internal/assets/embed/framework/types/composable.d.ts +14 -0
  84. package/create-fusee-go/internal/assets/embed/framework/types/directives.d.ts +55 -0
  85. package/create-fusee-go/internal/assets/embed/framework/types/evaluator.d.ts +13 -0
  86. package/create-fusee-go/internal/assets/embed/framework/types/event-delegation.d.ts +32 -0
  87. package/create-fusee-go/internal/assets/embed/framework/types/index.d.ts +22 -0
  88. package/create-fusee-go/internal/assets/embed/framework/types/router.d.ts +32 -0
  89. package/create-fusee-go/internal/assets/embed/framework/types/signal.d.ts +79 -0
  90. package/create-fusee-go/internal/assets/embed/framework/types/store.d.ts +8 -0
  91. package/create-fusee-go/internal/assets/templates/Counter.tmpl +26 -0
  92. package/create-fusee-go/internal/assets/templates/component.tmpl +11 -0
  93. package/create-fusee-go/internal/assets/templates/components/Loading.tmpl +24 -0
  94. package/create-fusee-go/internal/assets/templates/index.html.tmpl +15 -0
  95. package/create-fusee-go/internal/assets/templates/main.tmpl +14 -0
  96. package/create-fusee-go/internal/assets/templates/package.json.tmpl +14 -0
  97. package/create-fusee-go/internal/assets/templates/page.tmpl +12 -0
  98. package/create-fusee-go/internal/assets/templates/pages/about.tmpl +20 -0
  99. package/create-fusee-go/internal/assets/templates/pages/index.tmpl +22 -0
  100. package/create-fusee-go/internal/assets/templates/pages/layout.tmpl +18 -0
  101. package/create-fusee-go/internal/assets/templates/tsconfig.json.tmpl +14 -0
  102. package/create-fusee-go/internal/assets/templates/vite.config.js.tmpl +18 -0
  103. package/create-fusee-go/main.go +7 -0
  104. package/framework/auto-import-preset.js +13 -9
  105. package/framework/core/compiler.js +252 -249
  106. package/framework/core/component.js +279 -260
  107. package/framework/core/di.js +187 -0
  108. package/framework/core/directives.js +819 -786
  109. package/framework/core/evaluator.js +153 -70
  110. package/framework/core/event-delegation.js +33 -14
  111. package/framework/core/reconcile.js +151 -0
  112. package/framework/core/signal.js +679 -403
  113. package/framework/core/store.js +322 -81
  114. package/framework/index.js +12 -11
  115. package/framework/router/file-router.js +177 -0
  116. package/framework/router/router.js +554 -80
  117. package/framework/types/component.d.ts +89 -88
  118. package/framework/types/di.d.ts +43 -0
  119. package/framework/types/event-delegation.d.ts +0 -58
  120. package/framework/types/index.d.ts +32 -22
  121. package/framework/types/reconcile.d.ts +7 -0
  122. package/framework/types/router.d.ts +42 -15
  123. package/framework/types/signal.d.ts +147 -78
  124. package/framework/types/store.d.ts +66 -8
  125. package/index.html +638 -626
  126. package/package.json +5 -3
  127. package/tsconfig.json +1 -0
  128. package/vite.config.js +7 -1
  129. /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(unwrapSignals && typeof val === 'function' && val.isSignal ? val() : 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
- const body = isMultiStatement ? expr : `return ${expr}`
31
- const fn = new Function(...keys, body)
32
- return fn(...values)
33
- } catch (e) {
34
- console.warn(`[framework] Error evaluating "${expr}":`, e)
35
- return ''
36
- }
37
- }
38
-
39
- export function parseInterpolation(str) {
40
- const parts = []
41
- const regex = /\{\{\s*(.+?)\s*\}\}/g
42
- let lastIndex = 0
43
- let match
44
-
45
- while ((match = regex.exec(str)) !== null) {
46
- if (match.index > lastIndex) {
47
- parts.push({ type: 'static', value: str.slice(lastIndex, match.index) })
48
- }
49
- parts.push({ type: 'dynamic', key: match[1] })
50
- lastIndex = regex.lastIndex
51
- }
52
-
53
- if (lastIndex < str.length) {
54
- parts.push({ type: 'static', value: str.slice(lastIndex) })
55
- }
56
-
57
- return parts
58
- }
59
-
60
- export function sanitizeAttr(name, value) {
61
- const attrName = name.toLowerCase()
62
- if (SENSITIVE_ATTRS.includes(attrName)) {
63
- const trimmed = value.trim()
64
- if (DANGEROUS_SCHEMES.test(trimmed)) {
65
- console.warn(`[framework] Blocked potential XSS on ${name}: "${value}"`)
66
- return 'about:blank'
67
- }
68
- }
69
- return value
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
- let timeoutId
98
- let lastRun = 0
99
- let throttleTimeoutId
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
- lastRun = now
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
- throttleTimeoutId = setTimeout(() => {
158
- lastRun = Date.now()
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
- timeoutId = setTimeout(() => executeLogic(e), debounceMs)
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
+ }