@flamework-experimental/core 2.0.0-alpha.3 → 2.0.0-alpha.4
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 +11 -6
- package/docs/README.md +63 -0
- package/docs/guide/01-getting-started.md +334 -0
- package/docs/guide/02-modules.md +254 -0
- package/docs/guide/03-providers.md +423 -0
- package/docs/guide/04-lifecycle-events.md +423 -0
- package/docs/guide/05-components.md +793 -0
- package/docs/guide/06-networking.md +614 -0
- package/docs/guide/07-macros.md +332 -0
- package/docs/guide/08-plugins.md +203 -0
- package/docs/guide/09-project-structure.md +392 -0
- package/docs/guide/10-migrating-from-v1.md +573 -0
- package/docs/guide/11-scopes.md +165 -0
- package/docs/guide/12-testing.md +342 -0
- package/flamework.build +1 -1
- package/out/index.d.ts +1 -0
- package/out/init.luau +1 -0
- package/out/module/module.luau +1 -1
- package/out/module/moduleBuilder.luau +1 -1
- package/out/utility/getClassesInPath.d.ts +27 -1
- package/out/utility/getClassesInPath.luau +101 -19
- package/out/utility/pathRoot.d.ts +20 -1
- package/out/utility/pathRoot.luau +80 -4
- package/package.json +14 -7
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
local TS = _G[script]
|
|
3
|
+
local RunService = TS.import(script, TS.getModule(script, "@rbxts", "services")).RunService
|
|
3
4
|
local tsImport = TS.import(script, script.Parent, "tsImport").tsImport
|
|
4
5
|
local Reflect = TS.import(script, script.Parent.Parent, "reflect").Reflect
|
|
5
|
-
local
|
|
6
|
+
local _pathRoot = TS.import(script, script.Parent, "pathRoot")
|
|
7
|
+
local findRbxPath = _pathRoot.findRbxPath
|
|
8
|
+
local getPathRoot = _pathRoot.getPathRoot
|
|
9
|
+
local resolveRbxPath = _pathRoot.resolveRbxPath
|
|
6
10
|
local getModuleClasses = TS.import(script, script.Parent, "moduleClasses").getModuleClasses
|
|
11
|
+
--* How long {@link requireModules} waits for a folder that is not in the place, in seconds.
|
|
12
|
+
local MISSING_FOLDER_TIMEOUT = 5
|
|
13
|
+
--[[
|
|
14
|
+
*
|
|
15
|
+
* Why this realm cannot require a folder under `game` that belongs to the other realm, or nothing
|
|
16
|
+
* when it can: a client sees nothing of the server's containers, and the server has no
|
|
17
|
+
* `PlayerScripts` to take a client folder from.
|
|
18
|
+
|
|
19
|
+
]]
|
|
20
|
+
local function otherRealmReason(rbxPath)
|
|
21
|
+
if getPathRoot() ~= game then
|
|
22
|
+
return nil
|
|
23
|
+
end
|
|
24
|
+
local service = rbxPath[1]
|
|
25
|
+
if RunService:IsClient() and (service == "ServerScriptService" or service == "ServerStorage") then
|
|
26
|
+
return `the folder is in {service}, which does not replicate to clients. Call requireModules for it on the server`
|
|
27
|
+
end
|
|
28
|
+
if not RunService:IsClient() and service == "StarterPlayer" then
|
|
29
|
+
return "the folder is in StarterPlayer/StarterPlayerScripts, which only a client requires from, through its PlayerScripts. Call requireModules for it on the client"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
7
32
|
--[[
|
|
8
33
|
*
|
|
9
34
|
* Requires one ModuleScript through the roblox-ts runtime, as an `import` would, and returns what
|
|
@@ -23,23 +48,42 @@ local function importModule(moduleScript)
|
|
|
23
48
|
end
|
|
24
49
|
--[[
|
|
25
50
|
*
|
|
26
|
-
* Requires every ModuleScript at and under
|
|
51
|
+
* Requires every ModuleScript at and under an instance, in tree order, handing each to `visit` with
|
|
27
52
|
* what it exported.
|
|
28
53
|
|
|
29
54
|
]]
|
|
30
|
-
local function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
local preloadPath = resolveRbxPath(rbxPath)
|
|
34
|
-
if preloadPath:IsA("ModuleScript") then
|
|
35
|
-
visit(preloadPath, importModule(preloadPath))
|
|
55
|
+
local function loadModulesIn(root, visit)
|
|
56
|
+
if root:IsA("ModuleScript") then
|
|
57
|
+
visit(root, importModule(root))
|
|
36
58
|
end
|
|
37
|
-
for _, instance in
|
|
59
|
+
for _, instance in root:GetDescendants() do
|
|
38
60
|
if instance:IsA("ModuleScript") then
|
|
39
61
|
visit(instance, importModule(instance))
|
|
40
62
|
end
|
|
41
63
|
end
|
|
42
64
|
end
|
|
65
|
+
--[[
|
|
66
|
+
*
|
|
67
|
+
* Requires every ModuleScript at and under a Rojo path, in tree order, handing each to `visit` with
|
|
68
|
+
* what it exported. `caller` names the call that gave the path, for the warning a slow wait gets.
|
|
69
|
+
|
|
70
|
+
]]
|
|
71
|
+
local function loadModulesInPath(rbxPath, visit, caller)
|
|
72
|
+
local _rbxPath = rbxPath
|
|
73
|
+
assert(_rbxPath)
|
|
74
|
+
loadModulesIn(resolveRbxPath(rbxPath, caller), visit)
|
|
75
|
+
end
|
|
76
|
+
--* What every ModuleScript at and under an instance exported, leaving out the ones that exported nothing.
|
|
77
|
+
local function requireModulesIn(root)
|
|
78
|
+
local loaded = {}
|
|
79
|
+
loadModulesIn(root, function(_, value)
|
|
80
|
+
if value ~= nil then
|
|
81
|
+
local _value = value
|
|
82
|
+
table.insert(loaded, _value)
|
|
83
|
+
end
|
|
84
|
+
end)
|
|
85
|
+
return loaded
|
|
86
|
+
end
|
|
43
87
|
--[[
|
|
44
88
|
*
|
|
45
89
|
* Requires every ModuleScript at and under the specified Rojo path, in tree order, and returns
|
|
@@ -52,14 +96,48 @@ end
|
|
|
52
96
|
|
|
53
97
|
]]
|
|
54
98
|
local function requireModulesInPath(rbxPath)
|
|
55
|
-
local
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
99
|
+
local _rbxPath = rbxPath
|
|
100
|
+
assert(_rbxPath)
|
|
101
|
+
return requireModulesIn(resolveRbxPath(rbxPath))
|
|
102
|
+
end
|
|
103
|
+
--[[
|
|
104
|
+
*
|
|
105
|
+
* Requires every ModuleScript at and under a source folder, in tree order, for what the modules do
|
|
106
|
+
* as they load, and returns what each exported, leaving out the ones that exported nothing. This is
|
|
107
|
+
* v1's `Flamework.addPaths` for a folder that holds no providers: modules that register themselves
|
|
108
|
+
* with a library, such as commands.
|
|
109
|
+
*
|
|
110
|
+
* The path is a source path, as a string literal: `requireModules("src/server/commands")`. The build
|
|
111
|
+
* turns it into the Rojo path the folder ends up at, as it does for `registerProviders`, so the
|
|
112
|
+
* folder has to be in your Rojo project.
|
|
113
|
+
*
|
|
114
|
+
* Each module is required through the roblox-ts module cache, so it runs once, however many times
|
|
115
|
+
* it is required. A folder inside a folder that `registerProviders` registers needs no call:
|
|
116
|
+
* registration already requires every ModuleScript under it.
|
|
117
|
+
*
|
|
118
|
+
* Raises when a module fails to load, and when the folder is not in the place. A missing folder is
|
|
119
|
+
* waited for five seconds, once the place has loaded, and the error names the part that is missing.
|
|
120
|
+
* A folder of the other realm's -- a server folder on a client, a client folder on the server --
|
|
121
|
+
* raises at once, saying so.
|
|
122
|
+
*
|
|
123
|
+
* @metadata macro
|
|
124
|
+
|
|
125
|
+
]]
|
|
126
|
+
local function requireModules(path, rbxPath)
|
|
127
|
+
if rbxPath == nil then
|
|
128
|
+
error(`requireModules("{path}") was called without the folder's Rojo path, which the build fills in. ` .. "Call it directly, with a string literal, in code that the Flamework transformer compiles.", 2)
|
|
129
|
+
end
|
|
130
|
+
local otherRealm = otherRealmReason(rbxPath)
|
|
131
|
+
if otherRealm ~= nil then
|
|
132
|
+
error(`requireModules("{path}"): {otherRealm}.`, 2)
|
|
133
|
+
end
|
|
134
|
+
local _binding = findRbxPath(rbxPath, MISSING_FOLDER_TIMEOUT)
|
|
135
|
+
local found = _binding.found
|
|
136
|
+
local missing = _binding.missing
|
|
137
|
+
if missing ~= nil then
|
|
138
|
+
error(`requireModules("{path}"): the folder is not in the place. The build put it at {table.concat(rbxPath, "/")}, ` .. `and {found:GetFullName()} has no child named '{missing}' after {MISSING_FOLDER_TIMEOUT} seconds. ` .. "The path is misspelled or differs in case from the folder, or the folder is empty and missing from this " .. "clone, since git keeps no empty folder (the build warns about these where the path is used); or it was " .. "moved or renamed after the build; or the Rojo project the place was built from leaves it out.", 2)
|
|
139
|
+
end
|
|
140
|
+
return requireModulesIn(found)
|
|
63
141
|
end
|
|
64
142
|
--[[
|
|
65
143
|
*
|
|
@@ -76,9 +154,12 @@ end
|
|
|
76
154
|
*
|
|
77
155
|
* A module that fails to load raises, as it did in v1: a class that silently fails to register
|
|
78
156
|
* would otherwise only show up later as an unresolvable dependency, far from the cause.
|
|
157
|
+
*
|
|
158
|
+
* The folder is waited for as {@link resolveRbxPath} waits: `caller`, the registration that gave the
|
|
159
|
+
* path (`registerProviders("src/server/services")`), is named in the warning a slow wait gets.
|
|
79
160
|
|
|
80
161
|
]]
|
|
81
|
-
local function getClassesInPath(rbxPath)
|
|
162
|
+
local function getClassesInPath(rbxPath, caller)
|
|
82
163
|
local foundClasses = {}
|
|
83
164
|
local found = {}
|
|
84
165
|
-- Own metadata only: an undecorated subclass inherits its parent's identifier, and must not be
|
|
@@ -120,11 +201,12 @@ local function getClassesInPath(rbxPath)
|
|
|
120
201
|
for _, member in pairs(value) do
|
|
121
202
|
add(member)
|
|
122
203
|
end
|
|
123
|
-
end)
|
|
204
|
+
end, caller)
|
|
124
205
|
return foundClasses
|
|
125
206
|
end
|
|
126
207
|
return {
|
|
127
208
|
importModule = importModule,
|
|
128
209
|
requireModulesInPath = requireModulesInPath,
|
|
210
|
+
requireModules = requireModules,
|
|
129
211
|
getClassesInPath = getClassesInPath,
|
|
130
212
|
}
|
|
@@ -13,5 +13,24 @@ export declare function getPathRoot(): Instance;
|
|
|
13
13
|
*
|
|
14
14
|
* Under `game` the first segment names a service, and `StarterPlayer/StarterPlayerScripts` is
|
|
15
15
|
* answered from the local player's `PlayerScripts`, which is where that content actually runs.
|
|
16
|
+
*
|
|
17
|
+
* A child that is not there within five seconds (on a client, once the place has loaded) is warned
|
|
18
|
+
* about, naming `caller` -- the call that gave the path, such as `registerProviders("src/shared/components")`
|
|
19
|
+
* -- and the child missing, and then waited for without a limit: content that arrives late still
|
|
20
|
+
* resolves, as it always has. The warning comes once per path: a child further down that is late
|
|
21
|
+
* too is the same wait, and is waited for without a second one.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveRbxPath(rbxPath: readonly string[], caller?: string): Instance;
|
|
24
|
+
/**
|
|
25
|
+
* Walks a compile-time path as {@link resolveRbxPath} does, but gives up on a child that is not
|
|
26
|
+
* there instead of waiting for it forever. A client still loading the place waits for it to load
|
|
27
|
+
* first, since the place's content arrives as it loads; after that, a missing child is given
|
|
28
|
+
* `timeout` seconds to appear.
|
|
29
|
+
*
|
|
30
|
+
* Returns the instance the path names, or the deepest instance it found and the name missing below
|
|
31
|
+
* it.
|
|
16
32
|
*/
|
|
17
|
-
export declare function
|
|
33
|
+
export declare function findRbxPath(rbxPath: readonly string[], timeout: number): {
|
|
34
|
+
found: Instance;
|
|
35
|
+
missing?: string;
|
|
36
|
+
};
|
|
@@ -56,13 +56,14 @@ local function getPathRoot()
|
|
|
56
56
|
end
|
|
57
57
|
--[[
|
|
58
58
|
*
|
|
59
|
-
* Walks a compile-time path from {@link getPathRoot},
|
|
59
|
+
* Walks a compile-time path from {@link getPathRoot}, asking `child` for each segment in turn, and
|
|
60
|
+
* stops at the first segment it has no instance for.
|
|
60
61
|
*
|
|
61
62
|
* Under `game` the first segment names a service, and `StarterPlayer/StarterPlayerScripts` is
|
|
62
63
|
* answered from the local player's `PlayerScripts`, which is where that content actually runs.
|
|
63
64
|
|
|
64
65
|
]]
|
|
65
|
-
local function
|
|
66
|
+
local function walkRbxPath(rbxPath, child)
|
|
66
67
|
-- Copied so that a generated path literal is not consumed by this call.
|
|
67
68
|
local _array = {}
|
|
68
69
|
local _length = #_array
|
|
@@ -83,9 +84,83 @@ local function resolveRbxPath(rbxPath)
|
|
|
83
84
|
end
|
|
84
85
|
end
|
|
85
86
|
for _, segment in path do
|
|
86
|
-
|
|
87
|
+
local found = child(node, segment)
|
|
88
|
+
if found == nil then
|
|
89
|
+
return {
|
|
90
|
+
found = node,
|
|
91
|
+
missing = segment,
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
node = found
|
|
95
|
+
end
|
|
96
|
+
return {
|
|
97
|
+
found = node,
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
--[[
|
|
101
|
+
*
|
|
102
|
+
* How long a child of a path is waited for before the wait is warned about, in seconds: when the
|
|
103
|
+
* engine would warn of an infinite yield, which names neither the call nor the source path.
|
|
104
|
+
|
|
105
|
+
]]
|
|
106
|
+
local MISSING_CHILD_WARNING = 5
|
|
107
|
+
--* A child that is there now, or once a client has loaded the place and `timeout` seconds more have passed.
|
|
108
|
+
local function waitForChild(parent, name, timeout)
|
|
109
|
+
local child = parent:FindFirstChild(name)
|
|
110
|
+
if child ~= nil then
|
|
111
|
+
return child
|
|
112
|
+
end
|
|
113
|
+
-- A client receives the place's content as it loads, so its time only starts once it has loaded.
|
|
114
|
+
if RunService:IsClient() and not game:IsLoaded() then
|
|
115
|
+
game.Loaded:Wait()
|
|
87
116
|
end
|
|
88
|
-
return
|
|
117
|
+
return parent:WaitForChild(name, timeout)
|
|
118
|
+
end
|
|
119
|
+
--[[
|
|
120
|
+
*
|
|
121
|
+
* Walks a compile-time path from {@link getPathRoot}, waiting for each child in turn.
|
|
122
|
+
*
|
|
123
|
+
* Under `game` the first segment names a service, and `StarterPlayer/StarterPlayerScripts` is
|
|
124
|
+
* answered from the local player's `PlayerScripts`, which is where that content actually runs.
|
|
125
|
+
*
|
|
126
|
+
* A child that is not there within five seconds (on a client, once the place has loaded) is warned
|
|
127
|
+
* about, naming `caller` -- the call that gave the path, such as `registerProviders("src/shared/components")`
|
|
128
|
+
* -- and the child missing, and then waited for without a limit: content that arrives late still
|
|
129
|
+
* resolves, as it always has. The warning comes once per path: a child further down that is late
|
|
130
|
+
* too is the same wait, and is waited for without a second one.
|
|
131
|
+
|
|
132
|
+
]]
|
|
133
|
+
local function resolveRbxPath(rbxPath, caller)
|
|
134
|
+
local warned = false
|
|
135
|
+
return walkRbxPath(rbxPath, function(parent, name)
|
|
136
|
+
if warned then
|
|
137
|
+
return parent:WaitForChild(name)
|
|
138
|
+
end
|
|
139
|
+
local child = waitForChild(parent, name, MISSING_CHILD_WARNING)
|
|
140
|
+
if child ~= nil then
|
|
141
|
+
return child
|
|
142
|
+
end
|
|
143
|
+
warned = true
|
|
144
|
+
local waiting = if caller ~= nil then `{caller} is still waiting for its folder` else "Flamework is still waiting for a folder"
|
|
145
|
+
warn(`{waiting}: the build put it at {table.concat(rbxPath, "/")}, ` .. `and {parent:GetFullName()} has no child named '{name}' after {MISSING_CHILD_WARNING} seconds. ` .. "The path may be misspelled or differ in case from the folder, or the folder may be empty and missing from " .. "this clone, since git keeps no empty folder (the build warns about these where the path is used); or " .. "the folder was moved or renamed after the build, or the Rojo project the place was built from leaves it " .. "out. It keeps waiting.")
|
|
146
|
+
return parent:WaitForChild(name)
|
|
147
|
+
end).found
|
|
148
|
+
end
|
|
149
|
+
--[[
|
|
150
|
+
*
|
|
151
|
+
* Walks a compile-time path as {@link resolveRbxPath} does, but gives up on a child that is not
|
|
152
|
+
* there instead of waiting for it forever. A client still loading the place waits for it to load
|
|
153
|
+
* first, since the place's content arrives as it loads; after that, a missing child is given
|
|
154
|
+
* `timeout` seconds to appear.
|
|
155
|
+
*
|
|
156
|
+
* Returns the instance the path names, or the deepest instance it found and the name missing below
|
|
157
|
+
* it.
|
|
158
|
+
|
|
159
|
+
]]
|
|
160
|
+
local function findRbxPath(rbxPath, timeout)
|
|
161
|
+
return walkRbxPath(rbxPath, function(parent, name)
|
|
162
|
+
return waitForChild(parent, name, timeout)
|
|
163
|
+
end)
|
|
89
164
|
end
|
|
90
165
|
--[[
|
|
91
166
|
*
|
|
@@ -101,5 +176,6 @@ end
|
|
|
101
176
|
return {
|
|
102
177
|
getPathRoot = getPathRoot,
|
|
103
178
|
resolveRbxPath = resolveRbxPath,
|
|
179
|
+
findRbxPath = findRbxPath,
|
|
104
180
|
__setPathRoot = __setPathRoot,
|
|
105
181
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flamework-experimental/core",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.4",
|
|
4
4
|
"main": "out/init.luau",
|
|
5
5
|
"types": "out/index.d.ts",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "rbxtsc",
|
|
8
|
-
"watch": "rbxtsc -w"
|
|
9
|
-
},
|
|
10
6
|
"repository": {
|
|
11
7
|
"type": "git",
|
|
12
|
-
"url": "git+https://github.com/
|
|
8
|
+
"url": "git+https://github.com/Velover/ExperimentalFlameworkV2.git",
|
|
9
|
+
"directory": "packages/core"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/Velover/ExperimentalFlameworkV2#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Velover/ExperimentalFlameworkV2/issues"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "rbxtsc",
|
|
17
|
+
"watch": "rbxtsc -w",
|
|
18
|
+
"prepack": "node ../../scripts/copy-readme.mjs && node ../../scripts/copy-docs.mjs"
|
|
13
19
|
},
|
|
14
20
|
"files": [
|
|
15
21
|
"out",
|
|
16
|
-
"flamework.build"
|
|
22
|
+
"flamework.build",
|
|
23
|
+
"docs"
|
|
17
24
|
],
|
|
18
25
|
"publishConfig": {
|
|
19
26
|
"access": "public"
|