Skip to content

Commit 252913f

Browse files
committed
feat: optimize static element creation and instance factories across benchmarks and components
- Refactored static element and instance creation in `optimized-list.benchmark.luau`, `simple-performance.benchmark.luau`, and `counter.luau` for improved performance. - Updated static properties and instance factories to use consistent naming conventions. - Enhanced `flamework-class.luau` and `hello-world.story.luau` to utilize new static instance factories. - Improved `massive-list.benchmark.tsx` to simplify item count logic. - Added debug option to `tsconfig.json` for better transformer diagnostics. - Updated ESLint configuration to support multiple TypeScript projects. - Enhanced transformer to sanitize dependency types and manage type-only imports. - Added integration tests for transformer functionality, ensuring correct static element extraction and memoization behavior. - Introduced `tsconfig.vitest.json` for Vitest configuration.
1 parent b735cb5 commit 252913f

18 files changed

+763
-160
lines changed

demo/out/benchmark/massive-list.benchmark.luau

Lines changed: 161 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ local useFinePatchBlock = TS.import(script, game:GetService("ReplicatedStorage")
44
local _react = TS.import(script, game:GetService("ReplicatedStorage"), "rbxts_include", "node_modules", "@rbxts", "react")
55
local React = _react
66
local useState = _react.useState
7+
local useMemo = _react.useMemo
8+
local useVirtualList = TS.import(script, game:GetService("ReplicatedStorage"), "rbxts_include", "node_modules", "@decillion", "runtime", "out").useVirtualList
79
--[[
810
*
911
* Benchmark: Massive List Rendering
@@ -17,9 +19,11 @@ local function TraditionalListItem(_param)
1719
local name = _param.name
1820
local value = _param.value
1921
local isActive = _param.isActive
22+
local layoutOrder = _param.layoutOrder
2023
return React.createElement("frame", {
2124
Size = UDim2.new(1, 0, 0, 40),
2225
BackgroundColor3 = if isActive then Color3.fromRGB(100, 150, 100) else Color3.fromRGB(80, 80, 80),
26+
LayoutOrder = layoutOrder,
2327
}, React.createElement("textlabel", {
2428
Text = `{name}: {value}`,
2529
Size = UDim2.new(0.8, 0, 1, 0),
@@ -39,10 +43,12 @@ local function OptimizedListItem(_param)
3943
local name = _param.name
4044
local value = _param.value
4145
local isActive = _param.isActive
42-
return useFinePatchBlock(function(isActive, name, value, id)
46+
local layoutOrder = _param.layoutOrder
47+
return useFinePatchBlock(function(isActive, layoutOrder, name, value, id)
4348
return React.createElement("frame", {
4449
Size = UDim2.new(1, 0, 0, 40),
4550
BackgroundColor3 = if isActive then Color3.fromRGB(100, 150, 100) else Color3.fromRGB(80, 80, 80),
51+
LayoutOrder = layoutOrder,
4652
}, React.createElement("textlabel", {
4753
Text = `{name}: {value}`,
4854
Size = UDim2.new(0.8, 0, 1, 0),
@@ -55,13 +61,18 @@ local function OptimizedListItem(_param)
5561
TextColor3 = Color3.fromRGB(200, 200, 200),
5662
BackgroundTransparency = 1,
5763
}))
58-
end, { isActive, name, value, id }, { {
64+
end, { isActive, layoutOrder, name, value, id }, { {
5965
elementPath = {},
6066
edits = { {
6167
type = 8,
6268
dependencyKey = "isActive",
6369
propName = "BackgroundColor3",
6470
path = {},
71+
}, {
72+
type = 1,
73+
dependencyKey = "layoutOrder",
74+
propName = "LayoutOrder",
75+
path = {},
6576
} },
6677
}, {
6778
elementPath = { 0, 0 },
@@ -79,36 +90,40 @@ local function OptimizedListItem(_param)
7990
propName = "Text",
8091
path = { 0, 1 },
8192
} },
82-
} }, "dynamic_frame_73bz0ezrr")
93+
} }, "dynamic_frame_n9yr06b60")
8394
end
8495
local function MassiveListBenchmark()
8596
local itemCount, setItemCount = useState(1000)
8697
local useOptimized, setUseOptimized = useState(true)
8798
local updateTrigger, setUpdateTrigger = useState(0)
8899
local renderTime, setRenderTime = useState(0)
100+
local ITEM_HEIGHT = 45
89101
-- Generate test data
90-
local items = {}
91-
do
92-
local i = 0
93-
local _shouldIncrement = false
94-
while true do
95-
if _shouldIncrement then
96-
i += 1
97-
else
98-
_shouldIncrement = true
99-
end
100-
if not (i < itemCount) then
101-
break
102+
local items = useMemo(function()
103+
local generated = {}
104+
do
105+
local i = 0
106+
local _shouldIncrement = false
107+
while true do
108+
if _shouldIncrement then
109+
i += 1
110+
else
111+
_shouldIncrement = true
112+
end
113+
if not (i < itemCount) then
114+
break
115+
end
116+
local _arg0 = {
117+
id = i,
118+
name = `Item {i}`,
119+
value = math.random(1, 1000),
120+
isActive = math.random() > 0.7,
121+
}
122+
table.insert(generated, _arg0)
102123
end
103-
local _arg0 = {
104-
id = i,
105-
name = `Item {i}`,
106-
value = math.random(1, 1000),
107-
isActive = math.random() > 0.7,
108-
}
109-
table.insert(items, _arg0)
110124
end
111-
end
125+
return generated
126+
end, { itemCount, updateTrigger })
112127
local triggerUpdate = function()
113128
local startTime = tick()
114129
setUpdateTrigger(updateTrigger + 1)
@@ -119,7 +134,14 @@ local function MassiveListBenchmark()
119134
end)
120135
end
121136
local ListComponent = if useOptimized then OptimizedListItem else TraditionalListItem
122-
return useFinePatchBlock(function(itemCount, useOptimized, triggerUpdate, renderTime, items, ListComponent)
137+
local virtualList = useVirtualList({
138+
items = items,
139+
itemHeight = ITEM_HEIGHT,
140+
overscan = 6,
141+
})
142+
local baseLayoutOrder = virtualList.range.start * 2
143+
local visibleCount = #virtualList.visibleItems
144+
return useFinePatchBlock(function(itemCount, useOptimized, triggerUpdate, renderTime, baseLayoutOrder, virtualList, visibleCount)
123145
return React.createElement("frame", {
124146
Size = UDim2.new(1, 0, 1, 0),
125147
BackgroundColor3 = Color3.fromRGB(20, 20, 20),
@@ -158,12 +180,19 @@ local function MassiveListBenchmark()
158180
Position = UDim2.new(0.7, 0, 0, 35),
159181
TextColor3 = Color3.fromRGB(200, 200, 200),
160182
BackgroundTransparency = 1,
161-
}), React.createElement("textlabel", {
183+
}), React.createElement("textbutton", {
162184
Text = `Items: {itemCount}`,
163-
Size = UDim2.new(0.5, 0, 0, 30),
185+
Size = UDim2.new(0.3, 0, 0, 30),
164186
Position = UDim2.new(0, 0, 0, 70),
165-
TextColor3 = Color3.fromRGB(200, 200, 200),
166-
BackgroundTransparency = 1,
187+
BackgroundColor3 = Color3.fromRGB(80, 120, 180),
188+
TextColor3 = Color3.fromRGB(255, 255, 255),
189+
Event = {
190+
MouseButton1Click = function()
191+
return setItemCount(function(current)
192+
return if current >= 5000 then 1000 else current + 1000
193+
end)
194+
end,
195+
},
167196
}))
168197
end, { itemCount, useOptimized, triggerUpdate, renderTime }, { {
169198
elementPath = { 0, 0 },
@@ -210,52 +239,112 @@ local function MassiveListBenchmark()
210239
propName = "Text",
211240
path = { 0, 4 },
212241
} },
213-
} }, "dynamic_frame_286c8u4xu"), useFinePatchBlock(function(itemCount, items, ListComponent)
242+
} }, "dynamic_frame_l2to0lehr"), useFinePatchBlock(function(baseLayoutOrder, virtualList, visibleCount)
214243
local _exp = {
215244
Size = UDim2.new(1, 0, 1, -100),
216245
Position = UDim2.new(0, 0, 0, 100),
217246
BackgroundColor3 = Color3.fromRGB(30, 30, 30),
218-
CanvasSize = UDim2.new(0, 0, 0, itemCount * 45),
219247
ScrollBarThickness = 10,
220248
}
221249
local _exp_1 = React.createElement("uilistlayout", {
222250
SortOrder = Enum.SortOrder.LayoutOrder,
223251
Padding = UDim.new(0, 5),
224252
})
253+
local _exp_2 = useFinePatchBlock(function(baseLayoutOrder, virtualList)
254+
return React.createElement("frame", {
255+
LayoutOrder = baseLayoutOrder,
256+
Size = UDim2.new(1, 0, 0, virtualList.beforeSpacerHeight),
257+
BackgroundTransparency = 1,
258+
})
259+
end, { baseLayoutOrder, virtualList }, { {
260+
elementPath = {},
261+
edits = { {
262+
type = 1,
263+
dependencyKey = "baseLayoutOrder",
264+
propName = "LayoutOrder",
265+
path = {},
266+
}, {
267+
type = 1,
268+
dependencyKey = "virtualList",
269+
propName = "Size",
270+
path = {},
271+
} },
272+
} }, "dynamic_frame_96t5oo709")
273+
local _exp_3 = virtualList.visibleItems
225274
-- ▼ ReadonlyArray.map ▼
226-
local _newValue = table.create(#items)
227-
local _callback = function(item, index)
275+
local _newValue = table.create(#_exp_3)
276+
local _callback = function(virtualItem, visibleIndex)
277+
local _binding = virtualItem
278+
local item = _binding.item
228279
local _attributes = {
229-
key = `item-{item.id}-{updateTrigger}`,
280+
key = `item-{item.id}`,
230281
}
231282
for _k, _v in item do
232283
_attributes[_k] = _v
233284
end
285+
_attributes.layoutOrder = baseLayoutOrder + visibleIndex * 2 + 1
234286
return React.createElement(ListComponent, _attributes)
235287
end
236-
for _k, _v in items do
237-
_newValue[_k] = _callback(_v, _k - 1, items)
288+
for _k, _v in _exp_3 do
289+
_newValue[_k] = _callback(_v, _k - 1, _exp_3)
238290
end
239291
-- ▲ ReadonlyArray.map ▲
240-
return React.createElement("scrollingframe", _exp, _exp_1, _newValue)
241-
end, { itemCount, items, ListComponent }, { {
242-
elementPath = {},
292+
return React.createElement("scrollingframe", _exp, _exp_1, _exp_2, _newValue, useFinePatchBlock(function(baseLayoutOrder, visibleCount, virtualList)
293+
return React.createElement("frame", {
294+
LayoutOrder = baseLayoutOrder + visibleCount * 2 + 1,
295+
Size = UDim2.new(1, 0, 0, virtualList.afterSpacerHeight),
296+
BackgroundTransparency = 1,
297+
})
298+
end, { baseLayoutOrder, visibleCount, virtualList }, { {
299+
elementPath = {},
300+
edits = { {
301+
type = 1,
302+
dependencyKey = "baseLayoutOrder",
303+
propName = "LayoutOrder",
304+
path = {},
305+
}, {
306+
type = 1,
307+
dependencyKey = "virtualList",
308+
propName = "Size",
309+
path = {},
310+
} },
311+
} }, "dynamic_frame_ytscngc7q"))
312+
end, { baseLayoutOrder, virtualList, visibleCount }, { {
313+
elementPath = { 0, 1 },
243314
edits = { {
244315
type = 1,
245-
dependencyKey = "itemCount",
246-
propName = "CanvasSize",
247-
path = {},
316+
dependencyKey = "baseLayoutOrder",
317+
propName = "LayoutOrder",
318+
path = { 0, 1 },
319+
}, {
320+
type = 1,
321+
dependencyKey = "virtualList",
322+
propName = "Size",
323+
path = { 0, 1 },
248324
} },
249325
}, {
250326
elementPath = { 0 },
251327
edits = { {
252328
type = 2,
253-
dependencyKey = "items",
254-
index = 1,
255-
path = { 0, 1 },
329+
dependencyKey = "virtualList",
330+
index = 2,
331+
path = { 0, 2 },
256332
} },
257-
} }, "dynamic_scrollingframe_a3yjgudsn"))
258-
end, { itemCount, useOptimized, triggerUpdate, renderTime, items, ListComponent }, { {
333+
}, {
334+
elementPath = { 0, 3 },
335+
edits = { {
336+
type = 1,
337+
dependencyKey = "baseLayoutOrder",
338+
propName = "LayoutOrder",
339+
path = { 0, 3 },
340+
}, {
341+
type = 1,
342+
dependencyKey = "virtualList",
343+
propName = "Size",
344+
path = { 0, 3 },
345+
} },
346+
} }, "dynamic_scrollingframe_g68cmil6a"))
347+
end, { itemCount, useOptimized, triggerUpdate, renderTime, baseLayoutOrder, virtualList, visibleCount }, { {
259348
elementPath = { 0, 0, 0 },
260349
edits = { {
261350
type = 1,
@@ -301,22 +390,40 @@ local function MassiveListBenchmark()
301390
path = { 0, 0, 4 },
302391
} },
303392
}, {
304-
elementPath = { 0, 1 },
393+
elementPath = { 0, 1, 1 },
305394
edits = { {
306395
type = 1,
307-
dependencyKey = "itemCount",
308-
propName = "CanvasSize",
309-
path = { 0, 1 },
396+
dependencyKey = "baseLayoutOrder",
397+
propName = "LayoutOrder",
398+
path = { 0, 1, 1 },
399+
}, {
400+
type = 1,
401+
dependencyKey = "virtualList",
402+
propName = "Size",
403+
path = { 0, 1, 1 },
310404
} },
311405
}, {
312406
elementPath = { 0, 1 },
313407
edits = { {
314408
type = 2,
315-
dependencyKey = "items",
316-
index = 1,
317-
path = { 0, 1, 1 },
409+
dependencyKey = "virtualList",
410+
index = 2,
411+
path = { 0, 1, 2 },
412+
} },
413+
}, {
414+
elementPath = { 0, 1, 3 },
415+
edits = { {
416+
type = 1,
417+
dependencyKey = "baseLayoutOrder",
418+
propName = "LayoutOrder",
419+
path = { 0, 1, 3 },
420+
}, {
421+
type = 1,
422+
dependencyKey = "virtualList",
423+
propName = "Size",
424+
path = { 0, 1, 3 },
318425
} },
319-
} }, "dynamic_frame_0admk4o4r")
426+
} }, "dynamic_frame_64biizil8")
320427
end
321428
-- Alternative ways to use @undecillion
322429
-- Comment-style for arrow functions

0 commit comments

Comments
 (0)