@flamework-experimental/core 2.0.0-alpha.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 +66 -0
- package/flamework.build +17 -0
- package/out/dependency.d.ts +18 -0
- package/out/dependency.luau +32 -0
- package/out/flamework.d.ts +78 -0
- package/out/flamework.luau +138 -0
- package/out/index.d.ts +28 -0
- package/out/init.luau +40 -0
- package/out/injectable.d.ts +13 -0
- package/out/injectable.luau +23 -0
- package/out/lifecycle/lifecycleInterfaces.d.ts +94 -0
- package/out/lifecycle/lifecycleInterfaces.luau +55 -0
- package/out/lifecycle/lifecyclePlugin.d.ts +85 -0
- package/out/lifecycle/lifecyclePlugin.luau +424 -0
- package/out/modding.d.ts +171 -0
- package/out/modding.luau +66 -0
- package/out/module/defaultModule.d.ts +5 -0
- package/out/module/defaultModule.luau +28 -0
- package/out/module/module.d.ts +78 -0
- package/out/module/module.luau +811 -0
- package/out/module/moduleBuilder.d.ts +88 -0
- package/out/module/moduleBuilder.luau +169 -0
- package/out/module/moduleDefinition.d.ts +105 -0
- package/out/module/moduleDefinition.luau +79 -0
- package/out/module/moduleHooks.d.ts +23 -0
- package/out/module/moduleHooks.luau +19 -0
- package/out/module/providerRegistration.d.ts +22 -0
- package/out/module/providerRegistration.luau +80 -0
- package/out/module/scopes.d.ts +40 -0
- package/out/module/scopes.luau +159 -0
- package/out/plugin/pluginDefinition.d.ts +128 -0
- package/out/plugin/pluginDefinition.luau +61 -0
- package/out/prelude.d.ts +8 -0
- package/out/prelude.luau +14 -0
- package/out/provider.d.ts +27 -0
- package/out/provider.luau +26 -0
- package/out/reflect.d.ts +60 -0
- package/out/reflect.luau +310 -0
- package/out/serialization/types.d.ts +86 -0
- package/out/serialization/types.luau +9 -0
- package/out/utility/constructors.d.ts +4 -0
- package/out/utility/constructors.luau +12 -0
- package/out/utility/convertConciseDependencyInfo.d.ts +5 -0
- package/out/utility/convertConciseDependencyInfo.luau +29 -0
- package/out/utility/getClassImplements.d.ts +7 -0
- package/out/utility/getClassImplements.luau +27 -0
- package/out/utility/getClassesInPath.d.ts +23 -0
- package/out/utility/getClassesInPath.luau +91 -0
- package/out/utility/globs.d.ts +9 -0
- package/out/utility/globs.luau +64 -0
- package/out/utility/metadata.d.ts +12 -0
- package/out/utility/metadata.luau +43 -0
- package/out/utility/pathRoot.d.ts +17 -0
- package/out/utility/pathRoot.luau +105 -0
- package/out/utility/recycleThread.d.ts +1 -0
- package/out/utility/recycleThread.luau +31 -0
- package/out/utility/runtimeConfig.d.ts +59 -0
- package/out/utility/runtimeConfig.luau +25 -0
- package/out/utility/tsImport.d.ts +6 -0
- package/out/utility/tsImport.luau +15 -0
- package/out/utility/types.d.ts +16 -0
- package/out/utility/types.luau +22 -0
- package/out/utility/writable.d.ts +4 -0
- package/out/utility/writable.luau +2 -0
- package/package.json +33 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Modding } from "../modding";
|
|
2
|
+
import { ModuleDefinition, ProviderConfig, type IgniteOptions, type ProviderRegistrationOptions } from "./moduleDefinition";
|
|
3
|
+
import type { Constructor } from "../utility/constructors";
|
|
4
|
+
import { type PluginDefinition } from "../plugin/pluginDefinition";
|
|
5
|
+
import type { ScopeCondition } from "./scopes";
|
|
6
|
+
type GenericId<T> = string | Modding.Target.Id<T>;
|
|
7
|
+
export declare class ModuleBuilder {
|
|
8
|
+
/** A global count of the number of module builders. Used to disambiguate identical module debug names. */
|
|
9
|
+
private static moduleCount;
|
|
10
|
+
private moduleIndex;
|
|
11
|
+
private module;
|
|
12
|
+
constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Includes a plugin in this module: its setup runs against every ignition of the module, before
|
|
15
|
+
* any provider is constructed.
|
|
16
|
+
*
|
|
17
|
+
* A plugin is set up once per ignition however many times it is included, so including one
|
|
18
|
+
* twice is not an error; it is simply not recorded twice.
|
|
19
|
+
*
|
|
20
|
+
* With a scope condition, the plugin is set up only in a build where the condition holds, and
|
|
21
|
+
* is otherwise left out entirely, hooks and all.
|
|
22
|
+
*/
|
|
23
|
+
includePlugin(plugin: PluginDefinition, options?: ScopeCondition): this;
|
|
24
|
+
/**
|
|
25
|
+
* Leaves out the `LifecyclePlugin` every module otherwise starts with, so that nothing in this
|
|
26
|
+
* module receives `onInit`, `onStart` or the per-frame events. Silent by design: a module that
|
|
27
|
+
* wants no lifecycle has nothing to be told.
|
|
28
|
+
*/
|
|
29
|
+
disableDefaultLifecycle(): this;
|
|
30
|
+
/**
|
|
31
|
+
* Sets the debug name for this module.
|
|
32
|
+
*
|
|
33
|
+
* if a number is provided, a debug name will be generated using the debug info at the level (relative to the caller.)
|
|
34
|
+
*/
|
|
35
|
+
setDebugName(debugNameOrLevel: string | number): this;
|
|
36
|
+
/**
|
|
37
|
+
* Register all providers under the specified path and its descendants.
|
|
38
|
+
*
|
|
39
|
+
* The providers must be exported, and must carry the `@Provider()` decorator themselves: an
|
|
40
|
+
* undecorated subclass of a provider is not registered.
|
|
41
|
+
*
|
|
42
|
+
* The options apply to every provider found: a scope condition here scopes the whole folder.
|
|
43
|
+
*
|
|
44
|
+
* @metadata macro
|
|
45
|
+
*/
|
|
46
|
+
registerProviders<T extends string>(_stringPath: T, options?: ProviderRegistrationOptions, path?: Modding.Intrinsic<"path", [T], string[]>): this;
|
|
47
|
+
/**
|
|
48
|
+
* Register all providers under every path matched by the specified glob, which is resolved at
|
|
49
|
+
* compile time.
|
|
50
|
+
*
|
|
51
|
+
* This is the v2 equivalent of v1's `Flamework.addPathsGlob`. Globs can match a large number of
|
|
52
|
+
* paths, so keep them as specific as possible.
|
|
53
|
+
*
|
|
54
|
+
* @metadata macro
|
|
55
|
+
*/
|
|
56
|
+
registerProvidersGlob<T extends string>(_glob: T, options?: ProviderRegistrationOptions, glob?: Modding.Intrinsic<"pathglob", [T], string>): this;
|
|
57
|
+
private registerProviderClasses;
|
|
58
|
+
/**
|
|
59
|
+
* Register a new provider.
|
|
60
|
+
*
|
|
61
|
+
* Two registrations may share an id when their scope conditions keep at most one of them in
|
|
62
|
+
* any one build; both being kept is refused at ignition.
|
|
63
|
+
*
|
|
64
|
+
* @metadata macro
|
|
65
|
+
*/
|
|
66
|
+
registerProvider<T>(providerConfig: ProviderConfig, injectionId?: GenericId<T>): this;
|
|
67
|
+
/**
|
|
68
|
+
* Register a new class provider.
|
|
69
|
+
*
|
|
70
|
+
* This is just a shorthand for `registerProvider` which uses the generated `identifier` from the class.
|
|
71
|
+
*/
|
|
72
|
+
registerClassProvider(provider: Constructor, options?: ProviderRegistrationOptions): this;
|
|
73
|
+
/**
|
|
74
|
+
* An easy way to apply a function to the builder without breaking chaining.
|
|
75
|
+
*/
|
|
76
|
+
apply(callback: (builder: ModuleBuilder) => ModuleBuilder): ModuleBuilder;
|
|
77
|
+
/**
|
|
78
|
+
* Finalizes this module.
|
|
79
|
+
*/
|
|
80
|
+
build(): ModuleDefinition;
|
|
81
|
+
/**
|
|
82
|
+
* Ignites this module.
|
|
83
|
+
*
|
|
84
|
+
* This is shorthand for `.build().ignite(options)`
|
|
85
|
+
*/
|
|
86
|
+
ignite(options?: IgniteOptions): import("./module").Module;
|
|
87
|
+
}
|
|
88
|
+
export {};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local ModuleDefinition = TS.import(script, script.Parent, "moduleDefinition").ModuleDefinition
|
|
4
|
+
local getClassesInPath = TS.import(script, script.Parent.Parent, "utility", "getClassesInPath").getClassesInPath
|
|
5
|
+
local getClassesInGlob = TS.import(script, script.Parent.Parent, "utility", "globs").getClassesInGlob
|
|
6
|
+
local Reflect = TS.import(script, script.Parent.Parent, "reflect").Reflect
|
|
7
|
+
local LIFECYCLE_SLOT = TS.import(script, script.Parent.Parent, "plugin", "pluginDefinition").LIFECYCLE_SLOT
|
|
8
|
+
local _providerRegistration = TS.import(script, script.Parent, "providerRegistration")
|
|
9
|
+
local getProviderClassId = _providerRegistration.getProviderClassId
|
|
10
|
+
local normalizeProviderConfig = _providerRegistration.normalizeProviderConfig
|
|
11
|
+
local ModuleBuilder
|
|
12
|
+
do
|
|
13
|
+
ModuleBuilder = setmetatable({}, {
|
|
14
|
+
__tostring = function()
|
|
15
|
+
return "ModuleBuilder"
|
|
16
|
+
end,
|
|
17
|
+
})
|
|
18
|
+
ModuleBuilder.__index = ModuleBuilder
|
|
19
|
+
function ModuleBuilder.new(...)
|
|
20
|
+
local self = setmetatable({}, ModuleBuilder)
|
|
21
|
+
return self:constructor(...) or self
|
|
22
|
+
end
|
|
23
|
+
function ModuleBuilder:constructor()
|
|
24
|
+
local _original = ModuleBuilder.moduleCount
|
|
25
|
+
ModuleBuilder.moduleCount += 1
|
|
26
|
+
self.moduleIndex = _original
|
|
27
|
+
self.module = {
|
|
28
|
+
debugName = "Anonymous",
|
|
29
|
+
providers = {},
|
|
30
|
+
plugins = {},
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
function ModuleBuilder:includePlugin(plugin, options)
|
|
34
|
+
local plugins = self.module.plugins
|
|
35
|
+
-- ▼ ReadonlyArray.some ▼
|
|
36
|
+
local _result = false
|
|
37
|
+
local _callback = function(v)
|
|
38
|
+
return v.plugin == plugin
|
|
39
|
+
end
|
|
40
|
+
for _k, _v in plugins do
|
|
41
|
+
if _callback(_v, _k - 1, plugins) then
|
|
42
|
+
_result = true
|
|
43
|
+
break
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
-- ▲ ReadonlyArray.some ▲
|
|
47
|
+
if _result then
|
|
48
|
+
return self
|
|
49
|
+
end
|
|
50
|
+
-- A slotted plugin takes the place of whatever holds its slot -- the default lifecycle
|
|
51
|
+
-- plugin, usually -- rather than joining it, and keeps that position so hook order is stable.
|
|
52
|
+
local _result_1
|
|
53
|
+
if plugin.slot ~= nil then
|
|
54
|
+
-- ▼ ReadonlyArray.findIndex ▼
|
|
55
|
+
local _callback_1 = function(v)
|
|
56
|
+
return v.plugin.slot == plugin.slot
|
|
57
|
+
end
|
|
58
|
+
local _result_2 = -1
|
|
59
|
+
for _i, _v in plugins do
|
|
60
|
+
if _callback_1(_v, _i - 1, plugins) == true then
|
|
61
|
+
_result_2 = _i - 1
|
|
62
|
+
break
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
-- ▲ ReadonlyArray.findIndex ▲
|
|
66
|
+
_result_1 = _result_2
|
|
67
|
+
else
|
|
68
|
+
_result_1 = -1
|
|
69
|
+
end
|
|
70
|
+
local occupant = _result_1
|
|
71
|
+
local inclusion = {
|
|
72
|
+
plugin = plugin,
|
|
73
|
+
scope = options,
|
|
74
|
+
}
|
|
75
|
+
if occupant ~= -1 then
|
|
76
|
+
plugins[occupant + 1] = inclusion
|
|
77
|
+
else
|
|
78
|
+
table.insert(plugins, inclusion)
|
|
79
|
+
end
|
|
80
|
+
return self
|
|
81
|
+
end
|
|
82
|
+
function ModuleBuilder:disableDefaultLifecycle()
|
|
83
|
+
local plugins = self.module.plugins
|
|
84
|
+
for i = #plugins - 1, 0, -1 do
|
|
85
|
+
if plugins[i + 1].plugin.slot == LIFECYCLE_SLOT then
|
|
86
|
+
local _i = i
|
|
87
|
+
table.remove(plugins, _i + 1)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
return self
|
|
91
|
+
end
|
|
92
|
+
function ModuleBuilder:setDebugName(debugNameOrLevel)
|
|
93
|
+
local _debugNameOrLevel = debugNameOrLevel
|
|
94
|
+
if type(_debugNameOrLevel) == "string" then
|
|
95
|
+
self.module.debugName = debugNameOrLevel
|
|
96
|
+
else
|
|
97
|
+
local source, line = debug.info(debugNameOrLevel + 1, "sl")
|
|
98
|
+
local _exp = self.moduleIndex
|
|
99
|
+
local _condition = (string.match(source, "(%w+)$"))
|
|
100
|
+
if _condition == nil then
|
|
101
|
+
_condition = source
|
|
102
|
+
end
|
|
103
|
+
self.module.debugName = `{_exp}+{_condition}:{line}`
|
|
104
|
+
end
|
|
105
|
+
return self
|
|
106
|
+
end
|
|
107
|
+
function ModuleBuilder:registerProviders(_stringPath, options, path)
|
|
108
|
+
local _path = path
|
|
109
|
+
assert(_path)
|
|
110
|
+
return self:registerProviderClasses(getClassesInPath(path), options)
|
|
111
|
+
end
|
|
112
|
+
function ModuleBuilder:registerProvidersGlob(_glob, options, glob)
|
|
113
|
+
local _arg0 = glob ~= nil
|
|
114
|
+
assert(_arg0)
|
|
115
|
+
return self:registerProviderClasses(getClassesInGlob(glob), options)
|
|
116
|
+
end
|
|
117
|
+
function ModuleBuilder:registerProviderClasses(classes, options)
|
|
118
|
+
for _, provider in classes do
|
|
119
|
+
if Reflect.hasOwnMetadata(provider, "flamework:provider") then
|
|
120
|
+
self:registerClassProvider(provider, options)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
return self
|
|
124
|
+
end
|
|
125
|
+
function ModuleBuilder:registerProvider(providerConfig, injectionId)
|
|
126
|
+
local _arg0 = injectionId ~= nil
|
|
127
|
+
assert(_arg0)
|
|
128
|
+
local _providers = self.module.providers
|
|
129
|
+
local _arg0_1 = {
|
|
130
|
+
config = normalizeProviderConfig(providerConfig),
|
|
131
|
+
injectionId = injectionId,
|
|
132
|
+
}
|
|
133
|
+
table.insert(_providers, _arg0_1)
|
|
134
|
+
return self
|
|
135
|
+
end
|
|
136
|
+
function ModuleBuilder:registerClassProvider(provider, options)
|
|
137
|
+
local _result
|
|
138
|
+
if options ~= nil then
|
|
139
|
+
local _object = {
|
|
140
|
+
type = "class",
|
|
141
|
+
value = provider,
|
|
142
|
+
}
|
|
143
|
+
for _k, _v in options do
|
|
144
|
+
_object[_k] = _v
|
|
145
|
+
end
|
|
146
|
+
_result = _object
|
|
147
|
+
else
|
|
148
|
+
_result = {
|
|
149
|
+
type = "class",
|
|
150
|
+
value = provider,
|
|
151
|
+
}
|
|
152
|
+
end
|
|
153
|
+
local config = _result
|
|
154
|
+
return self:registerProvider(config, getProviderClassId(provider))
|
|
155
|
+
end
|
|
156
|
+
function ModuleBuilder:apply(callback)
|
|
157
|
+
return callback(self)
|
|
158
|
+
end
|
|
159
|
+
function ModuleBuilder:build()
|
|
160
|
+
return ModuleDefinition.new(self.module)
|
|
161
|
+
end
|
|
162
|
+
function ModuleBuilder:ignite(options)
|
|
163
|
+
return self:build():ignite(options)
|
|
164
|
+
end
|
|
165
|
+
ModuleBuilder.moduleCount = 0
|
|
166
|
+
end
|
|
167
|
+
return {
|
|
168
|
+
ModuleBuilder = ModuleBuilder,
|
|
169
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Modding } from "../modding";
|
|
2
|
+
import type { PluginDefinition } from "../plugin/pluginDefinition";
|
|
3
|
+
import { type Module } from "./module";
|
|
4
|
+
import type { ScopeCondition } from "./scopes";
|
|
5
|
+
/**
|
|
6
|
+
* Options for one ignition of a module.
|
|
7
|
+
*
|
|
8
|
+
* `activeIn` and `inactiveIn` are the module's own scope condition. It applies to every provider
|
|
9
|
+
* and component the module registers, on top of the registration's and the class's own conditions.
|
|
10
|
+
* A module whose condition does not hold still ignites, holding nothing.
|
|
11
|
+
*/
|
|
12
|
+
export interface IgniteOptions extends ScopeCondition {
|
|
13
|
+
/**
|
|
14
|
+
* Makes this module the one `Dependency<T>()` resolves against, replacing the current default.
|
|
15
|
+
*
|
|
16
|
+
* The first root module ignited in a realm becomes the default on its own, so a game never needs
|
|
17
|
+
* this. Pass it where a realm ignites more than one root -- tests, tools -- and a later one is the
|
|
18
|
+
* one `Dependency<T>()` should answer from.
|
|
19
|
+
*/
|
|
20
|
+
default?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Modules whose providers this one can inject and resolve, searched in order after its own,
|
|
23
|
+
* each through its own imports. Every one has to be ignited already.
|
|
24
|
+
*
|
|
25
|
+
* An import keeps its providers: their lifecycle, observers and extinguish stay with it, and
|
|
26
|
+
* this module only resolves them. An own registration of a class an import already resolves to
|
|
27
|
+
* is dropped in favour of the import's instance, unless it is `isolated`; a different class
|
|
28
|
+
* under the same id is kept, which is how a fake stands in for an import's provider here.
|
|
29
|
+
* Extinguishing an import extinguishes this module first.
|
|
30
|
+
*/
|
|
31
|
+
imports?: readonly Module[];
|
|
32
|
+
}
|
|
33
|
+
/** How a plugin was included: the plugin, and the condition its inclusion was given. */
|
|
34
|
+
export interface PluginInclusion {
|
|
35
|
+
readonly plugin: PluginDefinition;
|
|
36
|
+
/** The plugin is set up only while this holds; without one it always is. */
|
|
37
|
+
readonly scope?: ScopeCondition;
|
|
38
|
+
}
|
|
39
|
+
/** The configuration of the module. */
|
|
40
|
+
export interface ModuleState {
|
|
41
|
+
/** The debug name of this module. */
|
|
42
|
+
readonly debugName: string;
|
|
43
|
+
/** Contains all the included providers, as well as their configuration. */
|
|
44
|
+
readonly providers: readonly ModuleProvider[];
|
|
45
|
+
/** The plugins to set up on ignition, in inclusion order. */
|
|
46
|
+
readonly plugins: readonly PluginInclusion[];
|
|
47
|
+
}
|
|
48
|
+
export declare class ModuleDefinition {
|
|
49
|
+
private moduleState;
|
|
50
|
+
constructor(moduleState: ModuleState);
|
|
51
|
+
ignite(options?: IgniteOptions): Module;
|
|
52
|
+
}
|
|
53
|
+
export type ModuleProvider = {
|
|
54
|
+
config: ProviderConfig;
|
|
55
|
+
injectionId: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* What a registration can say about itself, whichever form it takes: an option on the class and
|
|
59
|
+
* path registrations, or written on the config of `registerProvider`.
|
|
60
|
+
*/
|
|
61
|
+
export interface ProviderRegistrationOptions extends ScopeCondition {
|
|
62
|
+
/**
|
|
63
|
+
* Keeps an own instance of a class that an imported module already holds. Without it, an own
|
|
64
|
+
* registration of the same class as one an import resolves to is dropped, and the import's
|
|
65
|
+
* instance answers. Class providers only.
|
|
66
|
+
*/
|
|
67
|
+
isolated?: boolean;
|
|
68
|
+
}
|
|
69
|
+
export type ProviderConfig = ProviderRegistrationOptions & ({
|
|
70
|
+
type: "class";
|
|
71
|
+
value: object;
|
|
72
|
+
/**
|
|
73
|
+
* A lazy class provider is not constructed during ignition. It is constructed the first
|
|
74
|
+
* time something resolves it, and is otherwise never created.
|
|
75
|
+
*
|
|
76
|
+
* Defaults to the `lazy` option of the class's `@Provider()` decorator, or `false`.
|
|
77
|
+
*/
|
|
78
|
+
lazy?: boolean;
|
|
79
|
+
} | {
|
|
80
|
+
type: "alias";
|
|
81
|
+
injectionId: string;
|
|
82
|
+
} | {
|
|
83
|
+
type: "function";
|
|
84
|
+
callback: (context: InjectionContext) => unknown;
|
|
85
|
+
});
|
|
86
|
+
export type InjectionContext = {
|
|
87
|
+
/**
|
|
88
|
+
* This is the ID of the dependency being requested.
|
|
89
|
+
*/
|
|
90
|
+
injectionId: string;
|
|
91
|
+
/**
|
|
92
|
+
* This is the dependency info for the requested dependency.
|
|
93
|
+
*/
|
|
94
|
+
dependencyInfo: Modding.DependencyInfo;
|
|
95
|
+
/**
|
|
96
|
+
* The module resolving the dependency, which is the one the provider is registered in.
|
|
97
|
+
*/
|
|
98
|
+
module: Module;
|
|
99
|
+
/**
|
|
100
|
+
* This is the class requesting the dependency.
|
|
101
|
+
*
|
|
102
|
+
* This can be used to retrieve information about the original class, such as the class name for a logging dependency.
|
|
103
|
+
*/
|
|
104
|
+
origin?: object;
|
|
105
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local _defaultModule = TS.import(script, script.Parent, "defaultModule")
|
|
4
|
+
local clearDefaultModule = _defaultModule.clearDefaultModule
|
|
5
|
+
local getDefaultModule = _defaultModule.getDefaultModule
|
|
6
|
+
local setDefaultModule = _defaultModule.setDefaultModule
|
|
7
|
+
local createModuleInstantiation = TS.import(script, script.Parent, "module").createModuleInstantiation
|
|
8
|
+
--[[
|
|
9
|
+
*
|
|
10
|
+
* Options for one ignition of a module.
|
|
11
|
+
*
|
|
12
|
+
* `activeIn` and `inactiveIn` are the module's own scope condition. It applies to every provider
|
|
13
|
+
* and component the module registers, on top of the registration's and the class's own conditions.
|
|
14
|
+
* A module whose condition does not hold still ignites, holding nothing.
|
|
15
|
+
|
|
16
|
+
]]
|
|
17
|
+
--* How a plugin was included: the plugin, and the condition its inclusion was given.
|
|
18
|
+
--* The configuration of the module.
|
|
19
|
+
local ModuleDefinition
|
|
20
|
+
do
|
|
21
|
+
ModuleDefinition = setmetatable({}, {
|
|
22
|
+
__tostring = function()
|
|
23
|
+
return "ModuleDefinition"
|
|
24
|
+
end,
|
|
25
|
+
})
|
|
26
|
+
ModuleDefinition.__index = ModuleDefinition
|
|
27
|
+
function ModuleDefinition.new(...)
|
|
28
|
+
local self = setmetatable({}, ModuleDefinition)
|
|
29
|
+
return self:constructor(...) or self
|
|
30
|
+
end
|
|
31
|
+
function ModuleDefinition:constructor(moduleState)
|
|
32
|
+
self.moduleState = moduleState
|
|
33
|
+
end
|
|
34
|
+
function ModuleDefinition:ignite(options)
|
|
35
|
+
local module = createModuleInstantiation(self.moduleState, options)
|
|
36
|
+
-- Claimed before ignition rather than after it, so that `Dependency<T>()` answers inside a
|
|
37
|
+
-- provider constructor, as it did in v1.
|
|
38
|
+
local previous = getDefaultModule()
|
|
39
|
+
local _result = options
|
|
40
|
+
if _result ~= nil then
|
|
41
|
+
_result = _result.default
|
|
42
|
+
end
|
|
43
|
+
local _condition = _result == true
|
|
44
|
+
if not _condition then
|
|
45
|
+
_condition = previous == nil
|
|
46
|
+
end
|
|
47
|
+
local claimsDefault = _condition
|
|
48
|
+
if claimsDefault then
|
|
49
|
+
setDefaultModule(module)
|
|
50
|
+
end
|
|
51
|
+
local _exitType, _returns = TS.try(function()
|
|
52
|
+
return TS.TRY_RETURN, { module.ignite() }
|
|
53
|
+
end, function(err)
|
|
54
|
+
-- A module that failed to ignite must not stay the default: the next root ignited would
|
|
55
|
+
-- never claim it, and `Dependency<T>()` would keep answering from the wreck. The root it
|
|
56
|
+
-- replaced is put back, if it is still ignited, so that a failed ignition leaves the
|
|
57
|
+
-- default as it found it.
|
|
58
|
+
if claimsDefault then
|
|
59
|
+
clearDefaultModule(module)
|
|
60
|
+
if previous ~= nil and previous.isIgnited() and getDefaultModule() == nil then
|
|
61
|
+
setDefaultModule(previous)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
error(err, 0)
|
|
65
|
+
end)
|
|
66
|
+
if _exitType then
|
|
67
|
+
return unpack(_returns)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
--[[
|
|
72
|
+
*
|
|
73
|
+
* What a registration can say about itself, whichever form it takes: an option on the class and
|
|
74
|
+
* path registrations, or written on the config of `registerProvider`.
|
|
75
|
+
|
|
76
|
+
]]
|
|
77
|
+
return {
|
|
78
|
+
ModuleDefinition = ModuleDefinition,
|
|
79
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conventional priorities for {@link HookOptions.priority}.
|
|
3
|
+
*
|
|
4
|
+
* Any number is accepted; these exist so that plugins can order themselves against each other
|
|
5
|
+
* without agreeing on magic numbers.
|
|
6
|
+
*/
|
|
7
|
+
export declare const HookPriority: {
|
|
8
|
+
/** Runs before hooks that did not specify a priority. */
|
|
9
|
+
readonly First: -1000;
|
|
10
|
+
/** The default. */
|
|
11
|
+
readonly Normal: 0;
|
|
12
|
+
/** Runs after hooks that did not specify a priority. */
|
|
13
|
+
readonly Last: 1000;
|
|
14
|
+
};
|
|
15
|
+
export interface HookOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Orders this hook against the other hooks of the same phase on the same module.
|
|
18
|
+
*
|
|
19
|
+
* Lower values run first, and hooks with an equal priority run in registration order.
|
|
20
|
+
* Defaults to {@link HookPriority.Normal}.
|
|
21
|
+
*/
|
|
22
|
+
priority?: number;
|
|
23
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
--[[
|
|
3
|
+
*
|
|
4
|
+
* Conventional priorities for {@link HookOptions.priority}.
|
|
5
|
+
*
|
|
6
|
+
* Any number is accepted; these exist so that plugins can order themselves against each other
|
|
7
|
+
* without agreeing on magic numbers.
|
|
8
|
+
|
|
9
|
+
]]
|
|
10
|
+
local HookPriority = {
|
|
11
|
+
First = -1000,
|
|
12
|
+
Normal = 0,
|
|
13
|
+
Last = 1000,
|
|
14
|
+
}
|
|
15
|
+
--* @internal
|
|
16
|
+
--* @internal
|
|
17
|
+
return {
|
|
18
|
+
HookPriority = HookPriority,
|
|
19
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Constructor } from "../utility/constructors";
|
|
2
|
+
import type { ProviderConfig } from "./moduleDefinition";
|
|
3
|
+
import { type ScopeCondition } from "./scopes";
|
|
4
|
+
/**
|
|
5
|
+
* Metadata is inherited through the class hierarchy, so this deliberately checks the class's own
|
|
6
|
+
* metadata: an undecorated subclass of a provider carries the parent's identifier, and registering
|
|
7
|
+
* it would register it under the parent's id.
|
|
8
|
+
*/
|
|
9
|
+
export declare function assertIsProviderClass(value: object): void;
|
|
10
|
+
/** The generated identifier a provider class is registered under. */
|
|
11
|
+
export declare function getProviderClassId(provider: Constructor): string;
|
|
12
|
+
/**
|
|
13
|
+
* Checks that a class provider carries `@Provider()`, and fills in `lazy` from the decorator when
|
|
14
|
+
* the registration did not say. Other kinds of provider are returned as they are.
|
|
15
|
+
*/
|
|
16
|
+
export declare function normalizeProviderConfig(config: ProviderConfig): ProviderConfig;
|
|
17
|
+
/**
|
|
18
|
+
* The scope condition a provider's own decorator set, when it is a class provider with one, and
|
|
19
|
+
* no condition otherwise. Own metadata, as everywhere else: a subclass does not inherit its
|
|
20
|
+
* parent's scope.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getProviderClassScope(config: ProviderConfig): ScopeCondition;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local Reflect = TS.import(script, script.Parent.Parent, "reflect").Reflect
|
|
4
|
+
local NO_CONDITION = TS.import(script, script.Parent, "scopes").NO_CONDITION
|
|
5
|
+
--[[
|
|
6
|
+
*
|
|
7
|
+
* Metadata is inherited through the class hierarchy, so this deliberately checks the class's own
|
|
8
|
+
* metadata: an undecorated subclass of a provider carries the parent's identifier, and registering
|
|
9
|
+
* it would register it under the parent's id.
|
|
10
|
+
|
|
11
|
+
]]
|
|
12
|
+
local function assertIsProviderClass(value)
|
|
13
|
+
if Reflect.hasOwnMetadata(value, "flamework:provider") then
|
|
14
|
+
return nil
|
|
15
|
+
end
|
|
16
|
+
if Reflect.hasMetadata(value, "flamework:provider") then
|
|
17
|
+
error(`class '{value}' is missing the @Provider() decorator: it inherits one from a parent class, but every provider must be decorated itself`)
|
|
18
|
+
end
|
|
19
|
+
error(`class '{value}' is missing the @Provider() decorator`)
|
|
20
|
+
end
|
|
21
|
+
--* The generated identifier a provider class is registered under.
|
|
22
|
+
local function getProviderClassId(provider)
|
|
23
|
+
assertIsProviderClass(provider)
|
|
24
|
+
local providerId = Reflect.getOwnMetadata(provider, "identifier")
|
|
25
|
+
local _arg0 = providerId ~= nil
|
|
26
|
+
local _arg1 = `class '{provider}' has no identifier, was it compiled with the Flamework transformer?`
|
|
27
|
+
assert(_arg0, _arg1)
|
|
28
|
+
return providerId
|
|
29
|
+
end
|
|
30
|
+
--[[
|
|
31
|
+
*
|
|
32
|
+
* Checks that a class provider carries `@Provider()`, and fills in `lazy` from the decorator when
|
|
33
|
+
* the registration did not say. Other kinds of provider are returned as they are.
|
|
34
|
+
|
|
35
|
+
]]
|
|
36
|
+
local function normalizeProviderConfig(config)
|
|
37
|
+
if config.type ~= "class" then
|
|
38
|
+
return config
|
|
39
|
+
end
|
|
40
|
+
assertIsProviderClass(config.value)
|
|
41
|
+
if config.lazy ~= nil then
|
|
42
|
+
return config
|
|
43
|
+
end
|
|
44
|
+
local decoratorConfig = Reflect.getOwnMetadata(config.value, "flamework:providerConfig")
|
|
45
|
+
local _object = table.clone(config)
|
|
46
|
+
setmetatable(_object, nil)
|
|
47
|
+
local _left = "lazy"
|
|
48
|
+
local _result = decoratorConfig
|
|
49
|
+
if _result ~= nil then
|
|
50
|
+
_result = _result.lazy
|
|
51
|
+
end
|
|
52
|
+
_object[_left] = _result == true
|
|
53
|
+
return _object
|
|
54
|
+
end
|
|
55
|
+
--[[
|
|
56
|
+
*
|
|
57
|
+
* The scope condition a provider's own decorator set, when it is a class provider with one, and
|
|
58
|
+
* no condition otherwise. Own metadata, as everywhere else: a subclass does not inherit its
|
|
59
|
+
* parent's scope.
|
|
60
|
+
|
|
61
|
+
]]
|
|
62
|
+
local function getProviderClassScope(config)
|
|
63
|
+
if config.type ~= "class" then
|
|
64
|
+
return NO_CONDITION
|
|
65
|
+
end
|
|
66
|
+
local decoratorConfig = Reflect.getOwnMetadata(config.value, "flamework:providerConfig")
|
|
67
|
+
if decoratorConfig == nil then
|
|
68
|
+
return NO_CONDITION
|
|
69
|
+
end
|
|
70
|
+
return {
|
|
71
|
+
activeIn = decoratorConfig.activeIn,
|
|
72
|
+
inactiveIn = decoratorConfig.inactiveIn,
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
return {
|
|
76
|
+
assertIsProviderClass = assertIsProviderClass,
|
|
77
|
+
getProviderClassId = getProviderClassId,
|
|
78
|
+
normalizeProviderConfig = normalizeProviderConfig,
|
|
79
|
+
getProviderClassScope = getProviderClassScope,
|
|
80
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When something is registered, in terms of the build's active scopes.
|
|
3
|
+
*
|
|
4
|
+
* A condition holds when `activeIn` is empty or names an active scope, and `inactiveIn` names
|
|
5
|
+
* none. Conditions combine by AND: a class is registered only when the module's condition, its
|
|
6
|
+
* registration's and its own all hold, so a class can narrow the module's condition and never
|
|
7
|
+
* widen it.
|
|
8
|
+
*/
|
|
9
|
+
export interface ScopeCondition {
|
|
10
|
+
/**
|
|
11
|
+
* Registered only while at least one of these scopes is active. Empty or absent means no
|
|
12
|
+
* requirement.
|
|
13
|
+
*/
|
|
14
|
+
activeIn?: readonly string[];
|
|
15
|
+
/** Never registered while any of these scopes is active. */
|
|
16
|
+
inactiveIn?: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The scopes this build is compiled with, as written in `flamework.config.json` (usually from
|
|
20
|
+
* the environment). `"*"` in the list stands for every scope.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getActiveScopes(): readonly string[];
|
|
23
|
+
/** Whether a scope is active in this build. */
|
|
24
|
+
export declare function isScopeActive(scope: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The condition of something that was not given one. Lists of conditions hold this rather than
|
|
27
|
+
* `undefined`: an array with a hole in it is not an array Luau can measure or walk.
|
|
28
|
+
*/
|
|
29
|
+
export declare const NO_CONDITION: ScopeCondition;
|
|
30
|
+
/** Whether a condition holds against the active scopes. No condition always holds. */
|
|
31
|
+
export declare function holdsCondition(condition: ScopeCondition | undefined): boolean;
|
|
32
|
+
/** Whether every condition holds. */
|
|
33
|
+
export declare function holdsEveryCondition(conditions: ReadonlyArray<ScopeCondition>): boolean;
|
|
34
|
+
/** Whether a condition asks for anything at all, so that a message can leave the empty ones out. */
|
|
35
|
+
export declare function hasCondition(condition: ScopeCondition | undefined): condition is ScopeCondition;
|
|
36
|
+
/**
|
|
37
|
+
* Describes the conditions that applied to something, and the active set they were judged
|
|
38
|
+
* against, for an error message: `activeIn [a, b]; inactiveIn [c]; active scopes [a]`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function describeConditions(conditions: ReadonlyArray<ScopeCondition>): string;
|