@quenty/loader 8.0.0-canary.ee17b33.0 → 8.0.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/CHANGELOG.md CHANGED
@@ -3,7 +3,59 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- # [8.0.0-canary.ee17b33.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/loader@7.1.0...@quenty/loader@8.0.0-canary.ee17b33.0) (2024-01-08)
6
+ # [8.0.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/loader@7.3.0...@quenty/loader@8.0.0) (2024-02-13)
7
+
8
+
9
+ ### Features
10
+
11
+ * New loader (breaking changes), fixing loader issues ([#439](https://github.com/Quenty/NevermoreEngine/issues/439)) ([3534345](https://github.com/Quenty/NevermoreEngine/commit/353434522918812953bd9f13fece73e27a4d034d))
12
+
13
+
14
+ ### BREAKING CHANGES
15
+
16
+ * Standard loader
17
+
18
+ Adds new loader version which replicates full structure instead of some partial structure. This allows us to have hot-reloading (in the future), as well as generally do less computation, handle dependencies more carefully, and other changes.
19
+
20
+ This means you'll need to change you how require client-side modules, as we export a simple `loader` module instead of all modules available.
21
+
22
+ Signed-off-by: James Onnen <jonnen0@gmail.com>
23
+
24
+ * fix: Fix missing dependency in ResetService
25
+
26
+ * feat: Add RxPhysicsUtils.observePartMass
27
+
28
+ * fix: Fix package discovery for games
29
+
30
+ * feat: Add UIAlignmentUtils.verticalToHorizontalAlignment(verticalAlignment) and UIAlignmentUtils.horizontalToVerticalAlignment(horizontalAlignment)
31
+
32
+ * feat: AdorneeData:InitAttributes() does not require data as a secondparameter
33
+
34
+ * ci: Upgrade to new rojo 7.4.0
35
+
36
+ * fix: Update loader to handle hoarcekat properly
37
+
38
+ * docs: Fix spacing in Maid
39
+
40
+ * fix: Add new ragdoll constants
41
+
42
+ * fix: Compress influxDB sends
43
+
44
+ * style: Errors use string.format
45
+
46
+ * fix: Handle motor animations
47
+
48
+ * ci: Upgrade rojo version
49
+
50
+ * feat!: Maid no longer is includd in ValueObject.Changed event
51
+
52
+ * docs: Fix docs
53
+
54
+
55
+
56
+
57
+
58
+ # [7.3.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/loader@7.1.0...@quenty/loader@7.3.0) (2024-01-08)
7
59
 
8
60
 
9
61
  ### Features
package/README.md CHANGED
@@ -56,8 +56,6 @@ For a given package folder, all packages underneath it shall have...
56
56
  3. Access to every package in the dependency folder at each ancestor level at the 1st level of recursion
57
57
  4. Access to every sibling package (this is because we expect to be installed at a uniform level)
58
58
 
59
- We will
60
-
61
59
 
62
60
 
63
61
  -------
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loader",
3
3
  "tree": {
4
- "$path": "src"
4
+ "$path": "src2"
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/loader",
3
- "version": "8.0.0-canary.ee17b33.0",
3
+ "version": "8.0.0",
4
4
  "description": "A simple module loader for Roblox",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -26,5 +26,5 @@
26
26
  "publishConfig": {
27
27
  "access": "public"
28
28
  },
29
- "gitHead": "ee17b3373035e250a9056103aee214c433ccf70e"
29
+ "gitHead": "5736b108e4d23cd4c9e761bbe4cc9fed6fb32dfa"
30
30
  }
@@ -8,5 +8,5 @@ local Utils = require(script.Parent.Utils)
8
8
  return Utils.readonly({
9
9
  GROUP_EACH_PACKAGE_INDIVIDUALLY = false;
10
10
  ALLOW_MULTIPLE_GROUPS = true;
11
- INCLUDE_IMPLICIT_DEPENDENCIES = true;
11
+ INCLUDE_IMPLICIT_DEPENDENCIES = false;
12
12
  })
@@ -45,9 +45,7 @@ function LoaderUtils.toWallyFormat(instance, isPlugin)
45
45
  PackageInfoUtils.fillDependencySet(packageInfoList)
46
46
 
47
47
  if isPlugin then
48
- local pluginGroup = GroupInfoUtils.groupPackageInfos(packageInfoList,
49
- ScriptInfoUtils.ModuleReplicationTypes.PLUGIN)
50
-
48
+ local pluginGroup = GroupInfoUtils.groupPackageInfos(packageInfoList, ScriptInfoUtils.ModuleReplicationTypes.PLUGIN)
51
49
  local publishSet = LoaderUtils.getPublishPackageInfoSet(packageInfoList)
52
50
 
53
51
  local pluginFolder = Instance.new("Folder")
@@ -57,13 +55,9 @@ function LoaderUtils.toWallyFormat(instance, isPlugin)
57
55
 
58
56
  return pluginFolder
59
57
  else
60
- local clientGroupList = GroupInfoUtils.groupPackageInfos(packageInfoList,
61
- ScriptInfoUtils.ModuleReplicationTypes.CLIENT)
62
- local serverGroupList = GroupInfoUtils.groupPackageInfos(packageInfoList,
63
- ScriptInfoUtils.ModuleReplicationTypes.SERVER)
64
- local sharedGroupList = GroupInfoUtils.groupPackageInfos(packageInfoList,
65
- ScriptInfoUtils.ModuleReplicationTypes.SHARED)
66
-
58
+ local clientGroupList = GroupInfoUtils.groupPackageInfos(packageInfoList, ScriptInfoUtils.ModuleReplicationTypes.CLIENT)
59
+ local serverGroupList = GroupInfoUtils.groupPackageInfos(packageInfoList, ScriptInfoUtils.ModuleReplicationTypes.SERVER)
60
+ local sharedGroupList = GroupInfoUtils.groupPackageInfos(packageInfoList, ScriptInfoUtils.ModuleReplicationTypes.SHARED)
67
61
  local publishSet = LoaderUtils.getPublishPackageInfoSet(packageInfoList)
68
62
 
69
63
  local clientFolder = Instance.new("Folder")
package/src/init.lua CHANGED
@@ -96,7 +96,7 @@ local function bootstrapPlugin(packageFolder)
96
96
  return require(pluginFolder:FindFirstChild(value))
97
97
  end
98
98
 
99
- error(("Unknown module %q"):format(tostring(value)))
99
+ error(string.format("Unknown module %q", tostring(value)))
100
100
  else
101
101
  return require(value)
102
102
  end
@@ -5,6 +5,10 @@
5
5
  @class DependencyUtils
6
6
  ]=]
7
7
 
8
+ local loader = script.Parent.Parent
9
+ local ReplicationType = require(loader.Replication.ReplicationType)
10
+ local ReplicationTypeUtils = require(loader.Replication.ReplicationTypeUtils)
11
+
8
12
  local DependencyUtils = {}
9
13
 
10
14
  --[=[
@@ -13,16 +17,22 @@ local DependencyUtils = {}
13
17
 
14
18
  @param requester Instance
15
19
  @param moduleName string
20
+ @param requestedReplicationType ReplicationType
16
21
  @return ModuleScript?
17
22
  ]=]
18
- function DependencyUtils.findDependency(requester, moduleName)
23
+ function DependencyUtils.findDependency(requester, moduleName, requestedReplicationType)
19
24
  assert(typeof(requester) == "Instance", "Bad requester")
20
25
  assert(type(moduleName) == "string", "Bad moduleName")
26
+ assert(ReplicationTypeUtils.isReplicationType(requestedReplicationType), "Bad requestedReplicationType")
21
27
 
22
28
  for packageInst in DependencyUtils.iterPackages(requester) do
23
- for module in DependencyUtils.iterModules(packageInst) do
29
+ for module, replicationType in DependencyUtils.iterModules(packageInst, ReplicationType.SHARED) do
24
30
  if module.Name == moduleName then
25
- return module
31
+ if ReplicationTypeUtils.isAllowed(replicationType, requestedReplicationType) then
32
+ return module
33
+ else
34
+ error(string.format("[DependencyUtils] - %q is not allowed in %q", moduleName, requestedReplicationType))
35
+ end
26
36
  end
27
37
  end
28
38
  end
@@ -30,20 +40,24 @@ function DependencyUtils.findDependency(requester, moduleName)
30
40
  return nil
31
41
  end
32
42
 
33
- function DependencyUtils.iterModules(packageInst)
43
+ function DependencyUtils.iterModules(packageInst, ancestorReplicationType)
34
44
  assert(typeof(packageInst) == "Instance", "Bad packageInst")
45
+ assert(ReplicationTypeUtils.isReplicationType(ancestorReplicationType), "Bad ancestorReplicationType")
35
46
 
36
47
  return coroutine.wrap(function()
37
48
  if packageInst:IsA("ModuleScript") then
38
- coroutine.yield(packageInst)
49
+ coroutine.yield(packageInst, ancestorReplicationType)
39
50
  return
40
51
  end
41
52
 
42
53
  -- Iterate over the package contents
43
54
  for _, item in pairs(packageInst:GetChildren()) do
44
- if item.Name ~= "node_modules" then
45
- for result in DependencyUtils.iterModules(item) do
46
- coroutine.yield(result)
55
+ local itemName = item.Name
56
+ local itemReplicationType = ReplicationTypeUtils.getFolderReplicationType(itemName, ancestorReplicationType)
57
+
58
+ if itemName ~= "node_modules" then
59
+ for result, resultReplicationType in DependencyUtils.iterModules(item, itemReplicationType) do
60
+ coroutine.yield(result, resultReplicationType)
47
61
  end
48
62
  end
49
63
  end
@@ -98,10 +112,10 @@ function DependencyUtils.iterPackagesInModuleModules(nodeModules)
98
112
  if linked:IsA("ModuleScript") or linked:IsA("Folder") then
99
113
  coroutine.yield(linked)
100
114
  else
101
- warn("Bad link value type")
115
+ warn("[DependencyUtils] - Bad link value type")
102
116
  end
103
117
  else
104
- warn("Nothing linked")
118
+ warn("[DependencyUtils] - Nothing linked")
105
119
  end
106
120
  end
107
121
  end
@@ -116,10 +130,10 @@ function DependencyUtils.iterPackagesInModuleModules(nodeModules)
116
130
  if linked:IsA("ModuleScript") or linked:IsA("Folder") then
117
131
  coroutine.yield(linked)
118
132
  else
119
- warn("Bad link value type")
133
+ warn("[DependencyUtils] - Bad link value type")
120
134
  end
121
135
  else
122
- warn("Nothing linked")
136
+ warn("[DependencyUtils] - Nothing linked")
123
137
  end
124
138
  end
125
139
  end
@@ -0,0 +1,441 @@
1
+ --[=[
2
+ For each package, track subdependent packages and packages
3
+
4
+ @class PackageTracker
5
+ ]=]
6
+
7
+ local loader = script.Parent.Parent
8
+ local Maid = require(loader.Maid)
9
+ local DependencyUtils = require(loader.Dependencies.DependencyUtils)
10
+ local ReplicationType = require(loader.Replication.ReplicationType)
11
+ local ReplicationTypeUtils = require(loader.Replication.ReplicationTypeUtils)
12
+
13
+ local PackageTracker = {}
14
+ PackageTracker.ClassName = "PackageTracker"
15
+ PackageTracker.__index = PackageTracker
16
+
17
+ function PackageTracker.new(packageTrackerProvider, packageRoot)
18
+ assert(packageTrackerProvider, "No packageTrackerProvider")
19
+ assert(typeof(packageRoot) == "Instance", "Bad packageRoot")
20
+
21
+ local self = setmetatable({}, PackageTracker)
22
+ self._maid = Maid.new()
23
+
24
+ self._packageTrackerProvider = assert(packageTrackerProvider, "No packageTrackerProvider")
25
+ self._packageRoot = assert(packageRoot, "No packageRoot")
26
+
27
+ self._subpackagesMap = {}
28
+ self._subpackagesTrackerList = {}
29
+ self._packageModuleScriptMap = {}
30
+
31
+ if self._packageRoot:IsA("ModuleScript") then
32
+ -- Module script children don't get to be observed
33
+ self._maid:GiveTask(self:_trackModuleScript(self._packageRoot, ReplicationType.SHARED))
34
+ else
35
+ self._maid:GiveTask(self:_trackChildren(self._packageRoot, ReplicationType.SHARED))
36
+ end
37
+
38
+ return self
39
+ end
40
+
41
+ function PackageTracker:ResolveDependency(request, replicationType)
42
+ local packageModuleScript = self:FindPackageModuleScript(request, replicationType)
43
+ if packageModuleScript then
44
+ return packageModuleScript
45
+ end
46
+
47
+ local subpackageModuleScript = self:FindSubpackageModuleScript(request, replicationType)
48
+ if subpackageModuleScript then
49
+ return subpackageModuleScript
50
+ end
51
+
52
+ local parentModuleScript = self:FindImplicitParentModuleScript(request, replicationType)
53
+ if parentModuleScript then
54
+ return parentModuleScript
55
+ end
56
+
57
+ return nil
58
+ end
59
+
60
+ function PackageTracker:FindImplicitParentModuleScript(request, replicationType)
61
+ assert(type(request) == "string", "Bad request")
62
+ assert(ReplicationTypeUtils.isReplicationType(replicationType), "Bad replicationType")
63
+
64
+ -- Implicit dependencies
65
+ local packageRootParent = self._packageRoot.Parent
66
+ if not packageRootParent then
67
+ return nil
68
+ end
69
+
70
+ local parentProvider = self._packageTrackerProvider:FindPackageTracker(packageRootParent)
71
+ if not parentProvider then
72
+ return nil
73
+ end
74
+
75
+ -- Check parent provider for implicit dependency
76
+ local subpackageModuleScript = parentProvider:FindSubpackageModuleScript(request, replicationType)
77
+ if subpackageModuleScript then
78
+ return subpackageModuleScript
79
+ end
80
+
81
+ return parentProvider:FindImplicitParentModuleScript(request, replicationType)
82
+ end
83
+
84
+ function PackageTracker:FindPackageModuleScript(moduleScriptName, replicationType)
85
+ assert(type(moduleScriptName) == "string", "Bad moduleScriptName")
86
+ assert(ReplicationTypeUtils.isReplicationType(replicationType), "Bad replicationType")
87
+
88
+ local found = self._packageModuleScriptMap[moduleScriptName]
89
+
90
+ if found then
91
+ if ReplicationTypeUtils.isAllowed(found.replicationType, replicationType) then
92
+ return found.moduleScript
93
+ else
94
+ return nil
95
+ end
96
+ else
97
+ return nil
98
+ end
99
+ end
100
+
101
+ function PackageTracker:FindSubpackageModuleScript(moduleScriptName, replicationType)
102
+ assert(type(moduleScriptName) == "string", "Bad moduleScriptName")
103
+ assert(ReplicationTypeUtils.isReplicationType(replicationType), "Bad replicationType")
104
+
105
+ for _, packageTracker in pairs(self._subpackagesTrackerList) do
106
+ local found = packageTracker._packageModuleScriptMap[moduleScriptName]
107
+ if found then
108
+ if ReplicationTypeUtils.isAllowed(found.replicationType, replicationType) then
109
+ return found.moduleScript
110
+ else
111
+ return nil
112
+ end
113
+ end
114
+ end
115
+
116
+ return nil
117
+ end
118
+
119
+ function PackageTracker:_trackChildrenAndReplicationType(parent, ancestorReplicationType)
120
+ assert(typeof(parent) == "Instance", "Bad parent")
121
+ assert(ReplicationTypeUtils.isReplicationType(ancestorReplicationType), "Bad ancestorReplicationType")
122
+
123
+ local maid = Maid.new()
124
+
125
+ local lastReplicationType = ReplicationTypeUtils.getFolderReplicationType(parent.Name, ancestorReplicationType)
126
+
127
+ maid:GiveTask(parent:GetPropertyChangedSignal("Name"):Connect(function()
128
+ local newReplicationType = ReplicationTypeUtils.getFolderReplicationType(parent.Name, ancestorReplicationType)
129
+ if newReplicationType ~= lastReplicationType then
130
+ maid._current = self:_trackChildren(parent, newReplicationType)
131
+ lastReplicationType = newReplicationType
132
+ end
133
+ end))
134
+
135
+ maid._current = self:_trackChildren(parent, lastReplicationType)
136
+
137
+ return maid
138
+ end
139
+
140
+ function PackageTracker:_trackChildren(parent, ancestorReplicationType)
141
+ assert(typeof(parent) == "Instance", "Bad parent")
142
+ assert(ReplicationTypeUtils.isReplicationType(ancestorReplicationType), "Bad ancestorReplicationType")
143
+
144
+ local maid = Maid.new()
145
+
146
+ maid:GiveTask(parent.ChildAdded:Connect(function(child)
147
+ self:_handleChildAdded(maid, child, ancestorReplicationType)
148
+ end))
149
+ maid:GiveTask(parent.ChildRemoved:Connect(function(child)
150
+ self:_handleChildRemoved(maid, child, ancestorReplicationType)
151
+ end))
152
+ for _, child in pairs(parent:GetChildren()) do
153
+ self:_handleChildAdded(maid, child, ancestorReplicationType)
154
+ end
155
+
156
+ return maid
157
+ end
158
+
159
+ function PackageTracker:_handleChildAdded(parentMaid, child, ancestorReplicationType)
160
+ assert(Maid.isMaid(parentMaid), "Bad maid")
161
+ assert(typeof(child) == "Instance", "Bad child")
162
+ assert(ReplicationTypeUtils.isReplicationType(ancestorReplicationType), "Bad ancestorReplicationType")
163
+
164
+ if child:IsA("ModuleScript") then
165
+ parentMaid[child] = self:_trackModuleScript(child, ancestorReplicationType)
166
+ elseif child:IsA("Folder") then
167
+ parentMaid[child] = self:_trackFolder(child, ancestorReplicationType)
168
+ end
169
+ end
170
+
171
+ function PackageTracker:_handleChildRemoved(parentMaid, child)
172
+ assert(Maid.isMaid(parentMaid), "Bad maid")
173
+ assert(typeof(child) == "Instance", "Bad child")
174
+
175
+ parentMaid[child] = nil
176
+ end
177
+
178
+
179
+ function PackageTracker:_trackFolder(child, ancestorReplicationType)
180
+ assert(typeof(child) == "Instance", "Bad child")
181
+ assert(ReplicationTypeUtils.isReplicationType(ancestorReplicationType), "Bad ancestorReplicationType")
182
+
183
+ local maid = Maid.new()
184
+
185
+ maid:GiveTask(child:GetPropertyChangedSignal("Name"):Connect(function()
186
+ if child.Name == "node_modules" then
187
+ maid._current = self:_trackMainNodeModuleFolder(child)
188
+ else
189
+ maid._current = self:_trackChildrenAndReplicationType(child, ancestorReplicationType)
190
+ end
191
+ end))
192
+
193
+ if child.Name == "node_modules" then
194
+ maid._current = self:_trackMainNodeModuleFolder(child)
195
+ else
196
+ maid._current = self:_trackChildrenAndReplicationType(child, ancestorReplicationType)
197
+ end
198
+
199
+ return maid
200
+ end
201
+
202
+
203
+ function PackageTracker:_trackModuleScript(child, ancestorReplicationType)
204
+ assert(typeof(child) == "Instance", "Bad child")
205
+ assert(ReplicationTypeUtils.isReplicationType(ancestorReplicationType), "Bad ancestorReplicationType")
206
+
207
+
208
+ local maid = Maid.new()
209
+
210
+ local function update()
211
+ if child.Archivable then
212
+ maid._current = self:_storeModuleScript(child.Name, child, ancestorReplicationType)
213
+ else
214
+ maid._current = nil
215
+ end
216
+ end
217
+
218
+ maid:GiveTask(child:GetPropertyChangedSignal("Name"):Connect(update))
219
+ maid:GiveTask(child:GetPropertyChangedSignal("Archivable"):Connect(update))
220
+
221
+ update()
222
+
223
+ return maid
224
+ end
225
+
226
+ function PackageTracker:_storeModuleScript(moduleScriptName, child, ancestorReplicationType)
227
+ assert(type(moduleScriptName) == "string", "Bad moduleScriptName")
228
+ assert(typeof(child) == "Instance", "Bad child")
229
+ assert(ReplicationTypeUtils.isReplicationType(ancestorReplicationType), "Bad ancestorReplicationType")
230
+
231
+ if self._packageModuleScriptMap[moduleScriptName] then
232
+ warn(string.format("[PackageTracker] - Overwriting moduleScript with name %q", moduleScriptName))
233
+ end
234
+
235
+ local data = {
236
+ moduleScript = child;
237
+ replicationType = ancestorReplicationType;
238
+ }
239
+ self._packageModuleScriptMap[moduleScriptName] = data
240
+
241
+ return function()
242
+ if self._packageModuleScriptMap[moduleScriptName] == data then
243
+ self._packageModuleScriptMap[moduleScriptName] = nil
244
+ end
245
+ end
246
+ end
247
+
248
+ function PackageTracker:_trackMainNodeModuleFolder(parent)
249
+ local maid = Maid.new()
250
+
251
+ maid:GiveTask(parent.ChildAdded:Connect(function(child)
252
+ self:_handleNodeModulesChildAdded(maid, child)
253
+ end))
254
+ maid:GiveTask(parent.ChildRemoved:Connect(function(child)
255
+ self:_handleNodeModulesChildRemoved(maid, child)
256
+ end))
257
+ for _, child in pairs(parent:GetChildren()) do
258
+ self:_handleNodeModulesChildAdded(maid, child)
259
+ end
260
+
261
+ return maid
262
+ end
263
+
264
+
265
+ function PackageTracker:_handleNodeModulesChildAdded(parentMaid, child)
266
+ assert(Maid.isMaid(parentMaid), "Bad maid")
267
+ assert(typeof(child) == "Instance", "Bad child")
268
+
269
+ if child:IsA("ObjectValue") then
270
+ -- Assume symlinked package
271
+ parentMaid[child] = self:_trackNodeModulesObjectValue(child)
272
+ elseif child:IsA("Folder") then
273
+ parentMaid[child] = self:_trackNodeModulesChildFolder(child)
274
+ elseif child:IsA("ModuleScript") then
275
+ parentMaid[child] = self:_trackAddPackage(child)
276
+ end
277
+ end
278
+
279
+ function PackageTracker:_handleNodeModulesChildRemoved(parentMaid, child)
280
+ assert(Maid.isMaid(parentMaid), "Bad maid")
281
+ assert(typeof(child) == "Instance", "Bad child")
282
+
283
+ parentMaid[child] = nil
284
+ end
285
+
286
+ function PackageTracker:_trackNodeModulesChildFolder(child)
287
+ assert(typeof(child) == "Instance", "Bad child")
288
+
289
+ local maid = Maid.new()
290
+
291
+ local function update()
292
+ local childName = child.Name
293
+
294
+ -- like @quenty
295
+ if DependencyUtils.isPackageGroup(childName) then
296
+ return self:_trackScopedChildFolder(childName, child)
297
+ else
298
+ return self:_tryStorePackage(childName, child)
299
+ end
300
+ end
301
+
302
+ maid:GiveTask(child:GetPropertyChangedSignal("Name"):Connect(function()
303
+ maid._current = update()
304
+ end))
305
+
306
+ maid._current = update()
307
+
308
+ return maid
309
+ end
310
+
311
+ function PackageTracker:_trackNodeModulesObjectValue(objectValue)
312
+ assert(typeof(objectValue) == "Instance", "Bad objectValue")
313
+
314
+ local maid = Maid.new()
315
+
316
+ maid:GiveTask(objectValue:GetPropertyChangedSignal("Name"):Connect(function()
317
+ maid._current = self:_tryStorePackage(objectValue.Name, objectValue.Value)
318
+ end))
319
+ maid:GiveTask(objectValue:GetPropertyChangedSignal("Value"):Connect(function()
320
+ maid._current = self:_tryStorePackage(objectValue.Name, objectValue.Value)
321
+ end))
322
+
323
+ maid._current = self:_tryStorePackage(objectValue.Name, objectValue.Value)
324
+
325
+ return maid
326
+ end
327
+
328
+ function PackageTracker:_trackScopedChildFolder(scopeName, parent)
329
+ assert(type(scopeName) == "string", "Bad scopeName")
330
+ assert(typeof(parent) == "Instance", "Bad parent")
331
+
332
+ local maid = Maid.new()
333
+
334
+ maid:GiveTask(parent.ChildAdded:Connect(function(child)
335
+ self:_handleScopedModulesChildAdded(scopeName, maid, child)
336
+ end))
337
+ maid:GiveTask(parent.ChildRemoved:Connect(function(child)
338
+ self:_handleScopedModulesChildRemoved(maid, child)
339
+ end))
340
+ for _, child in pairs(parent:GetChildren()) do
341
+ self:_handleScopedModulesChildAdded(scopeName, maid, child)
342
+ end
343
+
344
+ return maid
345
+ end
346
+
347
+ function PackageTracker:_handleScopedModulesChildAdded(scopeName, parentMaid, child)
348
+ assert(type(scopeName) == "string", "Bad scopeName")
349
+ assert(Maid.isMaid(parentMaid), "Bad maid")
350
+ assert(typeof(child) == "Instance", "Bad child")
351
+
352
+ if child:IsA("ObjectValue") then
353
+ parentMaid[child] = self:_trackScopedNodeModulesObjectValue(scopeName, child)
354
+ elseif child:IsA("Folder") or child:IsA("ModuleScript") then
355
+ parentMaid[child] = self:_trackAddScopedPackage(scopeName, child)
356
+ end
357
+ end
358
+
359
+ function PackageTracker:_trackScopedNodeModulesObjectValue(scopeName, objectValue)
360
+ assert(type(scopeName) == "string", "Bad scopeName")
361
+ assert(typeof(objectValue) == "Instance", "Bad objectValue")
362
+
363
+ local maid = Maid.new()
364
+
365
+ maid:GiveTask(objectValue:GetPropertyChangedSignal("Name"):Connect(function()
366
+ maid._current = self:_tryStorePackage(scopeName .. "/" .. objectValue.Name, objectValue.Value)
367
+ end))
368
+ maid:GiveTask(objectValue:GetPropertyChangedSignal("Value"):Connect(function()
369
+ maid._current = self:_tryStorePackage(scopeName .. "/" .. objectValue.Name, objectValue.Value)
370
+ end))
371
+
372
+ maid._current = self:_tryStorePackage(scopeName .. "/" .. objectValue.Name, objectValue.Value)
373
+
374
+ return maid
375
+ end
376
+
377
+ function PackageTracker:_handleScopedModulesChildRemoved(parentMaid, child)
378
+ assert(Maid.isMaid(parentMaid), "Bad maid")
379
+ assert(typeof(child) == "Instance", "Bad child")
380
+
381
+ parentMaid[child] = nil
382
+ end
383
+
384
+ function PackageTracker:_trackAddScopedPackage(scopeName, child)
385
+ assert(type(scopeName) == "string", "Bad scopeName")
386
+ assert(typeof(child) == "Instance", "Bad child")
387
+
388
+ local maid = Maid.new()
389
+
390
+ maid:GiveTask(child:GetPropertyChangedSignal("Name"):Connect(function()
391
+ maid._current = self:_tryStorePackage(scopeName .. "/" .. child.Name, child)
392
+ end))
393
+
394
+ maid._current = self:_tryStorePackage(scopeName .. "/" .. child.Name, child)
395
+
396
+ return maid
397
+ end
398
+
399
+ function PackageTracker:_trackAddPackage(child)
400
+ assert(typeof(child) == "Instance", "Bad child")
401
+
402
+ local maid = Maid.new()
403
+
404
+ maid:GiveTask(child:GetPropertyChangedSignal("Name"):Connect(function()
405
+ maid._current = self:_tryStorePackage(child.Name, child)
406
+ end))
407
+
408
+ maid._current = self:_tryStorePackage(child.Name, child)
409
+
410
+ return maid
411
+ end
412
+
413
+ function PackageTracker:_tryStorePackage(fullPackageName, packageInst)
414
+ assert(type(fullPackageName) == "string", "Bad fullPackageName")
415
+
416
+ if not packageInst then
417
+ return nil
418
+ end
419
+
420
+ self._subpackagesMap[fullPackageName] = packageInst
421
+
422
+ local packageTracker = self._packageTrackerProvider:AddPackageRoot(packageInst)
423
+ table.insert(self._subpackagesTrackerList, packageTracker)
424
+
425
+ return function()
426
+ local index = table.find(self._subpackagesTrackerList, packageTracker)
427
+ if index then
428
+ table.remove(self._subpackagesTrackerList, packageTracker)
429
+ end
430
+
431
+ if self._subpackagesMap[fullPackageName] == packageInst then
432
+ self._subpackagesMap[fullPackageName] = nil
433
+ end
434
+ end
435
+ end
436
+
437
+ function PackageTracker:Destroy()
438
+ self._maid:DoCleaning()
439
+ end
440
+
441
+ return PackageTracker