@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.
Files changed (65) hide show
  1. package/README.md +66 -0
  2. package/flamework.build +17 -0
  3. package/out/dependency.d.ts +18 -0
  4. package/out/dependency.luau +32 -0
  5. package/out/flamework.d.ts +78 -0
  6. package/out/flamework.luau +138 -0
  7. package/out/index.d.ts +28 -0
  8. package/out/init.luau +40 -0
  9. package/out/injectable.d.ts +13 -0
  10. package/out/injectable.luau +23 -0
  11. package/out/lifecycle/lifecycleInterfaces.d.ts +94 -0
  12. package/out/lifecycle/lifecycleInterfaces.luau +55 -0
  13. package/out/lifecycle/lifecyclePlugin.d.ts +85 -0
  14. package/out/lifecycle/lifecyclePlugin.luau +424 -0
  15. package/out/modding.d.ts +171 -0
  16. package/out/modding.luau +66 -0
  17. package/out/module/defaultModule.d.ts +5 -0
  18. package/out/module/defaultModule.luau +28 -0
  19. package/out/module/module.d.ts +78 -0
  20. package/out/module/module.luau +811 -0
  21. package/out/module/moduleBuilder.d.ts +88 -0
  22. package/out/module/moduleBuilder.luau +169 -0
  23. package/out/module/moduleDefinition.d.ts +105 -0
  24. package/out/module/moduleDefinition.luau +79 -0
  25. package/out/module/moduleHooks.d.ts +23 -0
  26. package/out/module/moduleHooks.luau +19 -0
  27. package/out/module/providerRegistration.d.ts +22 -0
  28. package/out/module/providerRegistration.luau +80 -0
  29. package/out/module/scopes.d.ts +40 -0
  30. package/out/module/scopes.luau +159 -0
  31. package/out/plugin/pluginDefinition.d.ts +128 -0
  32. package/out/plugin/pluginDefinition.luau +61 -0
  33. package/out/prelude.d.ts +8 -0
  34. package/out/prelude.luau +14 -0
  35. package/out/provider.d.ts +27 -0
  36. package/out/provider.luau +26 -0
  37. package/out/reflect.d.ts +60 -0
  38. package/out/reflect.luau +310 -0
  39. package/out/serialization/types.d.ts +86 -0
  40. package/out/serialization/types.luau +9 -0
  41. package/out/utility/constructors.d.ts +4 -0
  42. package/out/utility/constructors.luau +12 -0
  43. package/out/utility/convertConciseDependencyInfo.d.ts +5 -0
  44. package/out/utility/convertConciseDependencyInfo.luau +29 -0
  45. package/out/utility/getClassImplements.d.ts +7 -0
  46. package/out/utility/getClassImplements.luau +27 -0
  47. package/out/utility/getClassesInPath.d.ts +23 -0
  48. package/out/utility/getClassesInPath.luau +91 -0
  49. package/out/utility/globs.d.ts +9 -0
  50. package/out/utility/globs.luau +64 -0
  51. package/out/utility/metadata.d.ts +12 -0
  52. package/out/utility/metadata.luau +43 -0
  53. package/out/utility/pathRoot.d.ts +17 -0
  54. package/out/utility/pathRoot.luau +105 -0
  55. package/out/utility/recycleThread.d.ts +1 -0
  56. package/out/utility/recycleThread.luau +31 -0
  57. package/out/utility/runtimeConfig.d.ts +59 -0
  58. package/out/utility/runtimeConfig.luau +25 -0
  59. package/out/utility/tsImport.d.ts +6 -0
  60. package/out/utility/tsImport.luau +15 -0
  61. package/out/utility/types.d.ts +16 -0
  62. package/out/utility/types.luau +22 -0
  63. package/out/utility/writable.d.ts +4 -0
  64. package/out/utility/writable.luau +2 -0
  65. 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;