@modular-component/core 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ # @modular-component/core
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - be1b38e: Initial release of the ModularComponent system
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright © Jérémie van der Sande
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
+ documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
5
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6
+ persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
+
10
+ The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the
11
+ warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or
12
+ copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise,
13
+ arising from, out of or in connection with the software or the use or other dealings in the Software.
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # `@modular-component/core`
2
+
3
+ Core system for creating a `ModularComponent` factory. Exports the `modularFactory`
4
+ builder, and necessary types for creating extensions.
5
+
6
+ ## Installation and usage
7
+
8
+ ```bash
9
+ yarn add @modular-component/core
10
+ ```
11
+
12
+ ```tsx
13
+ // Usage in apps
14
+ import { modularFactory } from '@modular-component/core'
15
+
16
+ const ModularComponent = modularFactory.build()
17
+
18
+ const MyComponent = ModularComponent().withRender(() => (
19
+ <div>Hello Modular!</div>
20
+ ))
21
+ ```
22
+
23
+ ```tsx
24
+ // Usage in extensions
25
+ import { createMethodRecord } from '@modular-component/core'
26
+
27
+ export const WithExtension = createMethodRecord({
28
+ field: 'extension',
29
+ } as const)
30
+ ```
31
+
32
+ ## Learn more
33
+
34
+ Read the [`ModularComponent` ReadMe](https://github.com/jvdsande/modular-component/blob/master/README.md) for more information about the `ModularComponent` system.
@@ -0,0 +1,144 @@
1
+ /// <reference types="react" />
2
+ import { MethodRecord, Modular, StageEntry } from './types';
3
+ export type { Modular, ModularStageTransform, MethodRecord } from './types';
4
+ export declare const modularFactory: {
5
+ build: <Stages extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props = {}>(displayName?: string) => Modular<Props, {
6
+ readonly withRender: {
7
+ readonly field: "render";
8
+ readonly restrict: import("react").ReactElement<any, any> | null;
9
+ };
10
+ }, Stages>;
11
+ extend: <_Methods extends MethodRecord>(_methods: _Methods) => {
12
+ build: <Stages_1 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_1 = {}>(displayName?: string) => Modular<Props_1, {
13
+ readonly withRender: {
14
+ readonly field: "render";
15
+ readonly restrict: import("react").ReactElement<any, any> | null;
16
+ };
17
+ } & _Methods extends infer T ? T extends {
18
+ readonly withRender: {
19
+ readonly field: "render";
20
+ readonly restrict: import("react").ReactElement<any, any> | null;
21
+ };
22
+ } & _Methods ? T extends infer U ? { [key in keyof U]: U[key]; } : never : never : never, Stages_1>;
23
+ extend: <_Methods_1 extends MethodRecord>(_methods: _Methods_1) => {
24
+ build: <Stages_2 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_2 = {}>(displayName?: string) => Modular<Props_2, {
25
+ readonly withRender: {
26
+ readonly field: "render";
27
+ readonly restrict: import("react").ReactElement<any, any> | null;
28
+ };
29
+ } & _Methods & _Methods_1 extends infer T_1 ? T_1 extends {
30
+ readonly withRender: {
31
+ readonly field: "render";
32
+ readonly restrict: import("react").ReactElement<any, any> | null;
33
+ };
34
+ } & _Methods & _Methods_1 ? T_1 extends infer U ? { [key_1 in keyof U]: U[key_1]; } : never : never : never, Stages_2>;
35
+ extend: <_Methods_2 extends MethodRecord>(_methods: _Methods_2) => {
36
+ build: <Stages_3 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_3 = {}>(displayName?: string) => Modular<Props_3, {
37
+ readonly withRender: {
38
+ readonly field: "render";
39
+ readonly restrict: import("react").ReactElement<any, any> | null;
40
+ };
41
+ } & _Methods & _Methods_1 & _Methods_2 extends infer T_2 ? T_2 extends {
42
+ readonly withRender: {
43
+ readonly field: "render";
44
+ readonly restrict: import("react").ReactElement<any, any> | null;
45
+ };
46
+ } & _Methods & _Methods_1 & _Methods_2 ? T_2 extends infer U ? { [key_2 in keyof U]: U[key_2]; } : never : never : never, Stages_3>;
47
+ extend: <_Methods_3 extends MethodRecord>(_methods: _Methods_3) => {
48
+ build: <Stages_4 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_4 = {}>(displayName?: string) => Modular<Props_4, {
49
+ readonly withRender: {
50
+ readonly field: "render";
51
+ readonly restrict: import("react").ReactElement<any, any> | null;
52
+ };
53
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 extends infer T_3 ? T_3 extends {
54
+ readonly withRender: {
55
+ readonly field: "render";
56
+ readonly restrict: import("react").ReactElement<any, any> | null;
57
+ };
58
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 ? T_3 extends infer U ? { [key_3 in keyof U]: U[key_3]; } : never : never : never, Stages_4>;
59
+ extend: <_Methods_4 extends MethodRecord>(_methods: _Methods_4) => {
60
+ build: <Stages_5 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_5 = {}>(displayName?: string) => Modular<Props_5, {
61
+ readonly withRender: {
62
+ readonly field: "render";
63
+ readonly restrict: import("react").ReactElement<any, any> | null;
64
+ };
65
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 extends infer T_4 ? T_4 extends {
66
+ readonly withRender: {
67
+ readonly field: "render";
68
+ readonly restrict: import("react").ReactElement<any, any> | null;
69
+ };
70
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 ? T_4 extends infer U ? { [key_4 in keyof U]: U[key_4]; } : never : never : never, Stages_5>;
71
+ extend: <_Methods_5 extends MethodRecord>(_methods: _Methods_5) => {
72
+ build: <Stages_6 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_6 = {}>(displayName?: string) => Modular<Props_6, {
73
+ readonly withRender: {
74
+ readonly field: "render";
75
+ readonly restrict: import("react").ReactElement<any, any> | null;
76
+ };
77
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 extends infer T_5 ? T_5 extends {
78
+ readonly withRender: {
79
+ readonly field: "render";
80
+ readonly restrict: import("react").ReactElement<any, any> | null;
81
+ };
82
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 ? T_5 extends infer U ? { [key_5 in keyof U]: U[key_5]; } : never : never : never, Stages_6>;
83
+ extend: <_Methods_6 extends MethodRecord>(_methods: _Methods_6) => {
84
+ build: <Stages_7 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_7 = {}>(displayName?: string) => Modular<Props_7, {
85
+ readonly withRender: {
86
+ readonly field: "render";
87
+ readonly restrict: import("react").ReactElement<any, any> | null;
88
+ };
89
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 & _Methods_6 extends infer T_6 ? T_6 extends {
90
+ readonly withRender: {
91
+ readonly field: "render";
92
+ readonly restrict: import("react").ReactElement<any, any> | null;
93
+ };
94
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 & _Methods_6 ? T_6 extends infer U ? { [key_6 in keyof U]: U[key_6]; } : never : never : never, Stages_7>;
95
+ extend: <_Methods_7 extends MethodRecord>(_methods: _Methods_7) => {
96
+ build: <Stages_8 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_8 = {}>(displayName?: string) => Modular<Props_8, {
97
+ readonly withRender: {
98
+ readonly field: "render";
99
+ readonly restrict: import("react").ReactElement<any, any> | null;
100
+ };
101
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 & _Methods_6 & _Methods_7 extends infer T_7 ? T_7 extends {
102
+ readonly withRender: {
103
+ readonly field: "render";
104
+ readonly restrict: import("react").ReactElement<any, any> | null;
105
+ };
106
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 & _Methods_6 & _Methods_7 ? T_7 extends infer U ? { [key_7 in keyof U]: U[key_7]; } : never : never : never, Stages_8>;
107
+ extend: <_Methods_8 extends MethodRecord>(_methods: _Methods_8) => {
108
+ build: <Stages_9 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_9 = {}>(displayName?: string) => Modular<Props_9, {
109
+ readonly withRender: {
110
+ readonly field: "render";
111
+ readonly restrict: import("react").ReactElement<any, any> | null;
112
+ };
113
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 & _Methods_6 & _Methods_7 & _Methods_8 extends infer T_8 ? T_8 extends {
114
+ readonly withRender: {
115
+ readonly field: "render";
116
+ readonly restrict: import("react").ReactElement<any, any> | null;
117
+ };
118
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 & _Methods_6 & _Methods_7 & _Methods_8 ? T_8 extends infer U ? { [key_8 in keyof U]: U[key_8]; } : never : never : never, Stages_9>;
119
+ extend: <_Methods_9 extends MethodRecord>(_methods: _Methods_9) => {
120
+ build: <Stages_10 extends StageEntry[] = []>(stages?: Omit<StageEntry, 'stages'>[]) => <Props_10 = {}>(displayName?: string) => Modular<Props_10, {
121
+ readonly withRender: {
122
+ readonly field: "render";
123
+ readonly restrict: import("react").ReactElement<any, any> | null;
124
+ };
125
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 & _Methods_6 & _Methods_7 & _Methods_8 & _Methods_9 extends infer T_9 ? T_9 extends {
126
+ readonly withRender: {
127
+ readonly field: "render";
128
+ readonly restrict: import("react").ReactElement<any, any> | null;
129
+ };
130
+ } & _Methods & _Methods_1 & _Methods_2 & _Methods_3 & _Methods_4 & _Methods_5 & _Methods_6 & _Methods_7 & _Methods_8 & _Methods_9 ? T_9 extends infer U ? { [key_9 in keyof U]: U[key_9]; } : never : never : never, Stages_10>;
131
+ extend: <_Methods_10 extends MethodRecord>(_methods: _Methods_10) => any;
132
+ };
133
+ };
134
+ };
135
+ };
136
+ };
137
+ };
138
+ };
139
+ };
140
+ };
141
+ };
142
+ };
143
+ export declare function createMethodRecord<R extends MethodRecord>(record: R): R;
144
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,OAAO,EAAc,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGvE,YAAY,EAAE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAsH3E,eAAO,MAAM,cAAc;uDA7Gb,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,gCAEF,MAAM;;;;;;;6DAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,kCAEF,MAAM;;;;;;;;;;;;iEAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,kCAEF,MAAM;;;;;;;;;;;;qEAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,kCAEF,MAAM;;;;;;;;;;;;yEAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,kCAEF,MAAM;;;;;;;;;;;;6EAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,kCAEF,MAAM;;;;;;;;;;;;iFAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,kCAEF,MAAM;;;;;;;;;;;;qFAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,kCAEF,MAAM;;;;;;;;;;;;yFAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,kCAEF,MAAM;;;;;;;;;;;;6FAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,kCAEF,MAAM;;;;;;;;;;;;kGAFhC,KAAK,UAAU,EAAE,QAAQ,CAAC,EAAE,mCAEF,MAAM;;;;;;;;;;;;;;;;;;;;;;CAgHnC,CAAA;AAEX,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEvE"}
package/dist/index.js ADDED
@@ -0,0 +1,90 @@
1
+ function ModularFactory(methods) {
2
+ return {
3
+ build: (stages = []) => {
4
+ return (displayName) => {
5
+ // Create the actual Component. This is a simple React Functional Component
6
+ // that will call the hooks for each registered stages in order.
7
+ const Component = ((props) => {
8
+ // Prepare the shared arguments object, prefilling it with the props
9
+ // and an empty render result
10
+ const useComponent = Component.asHook();
11
+ const args = useComponent(props);
12
+ return args.render ?? null;
13
+ });
14
+ // Set the debug display name if provided
15
+ Component.displayName = displayName;
16
+ // Add an asHook system to get the components args as a reusable hook
17
+ Component.asHook = ((field) => (props) => {
18
+ // Prepare the shared arguments object, prefilling it with the props
19
+ // and an empty render result
20
+ let args = { props, render: null };
21
+ // Run each stage in order, replacing the arguments by the response
22
+ // from the last stage
23
+ for (const stage of stages) {
24
+ const method = methods[stage.key];
25
+ const useStage = stage.value;
26
+ const useTransform = method.transform ??
27
+ (() => typeof useStage === 'function'
28
+ ? useStage({ ...args })
29
+ : useStage);
30
+ args = {
31
+ ...args,
32
+ [method.field]: useTransform(args, useStage),
33
+ };
34
+ }
35
+ // Finally, return the args
36
+ return field ? args[field] : args;
37
+ });
38
+ // Add a function for rewinding the component up to a certain stage
39
+ Component.atStage = ((stage) => {
40
+ // Find the needed stage
41
+ const stageIndex = stages.findIndex((record) => record.key === stage);
42
+ // If the stage cannot be found, create a brand new, empty component
43
+ if (stageIndex === -1) {
44
+ return ModularFactory(methods).build()(displayName);
45
+ }
46
+ // Otherwise, keep all stages up to and including the found stage
47
+ return ModularFactory(methods).build(stages.slice(0, stageIndex + 1))(displayName);
48
+ });
49
+ // Add each configured stage method to the component
50
+ Object.keys(methods).forEach((method) => {
51
+ Component[method] = ((value) => {
52
+ // Prepare the new stage
53
+ const stage = { key: method, value };
54
+ // For stages in "multiple" mode, simply append the stage
55
+ if (methods[method].multiple) {
56
+ return ModularFactory(methods).build([...stages, stage])(displayName);
57
+ }
58
+ // For other stages, check if a stage of the same key already exists
59
+ const index = stages.findIndex((st) => st.key === method);
60
+ // If so, copy the stages and replace the previous record
61
+ if (index > -1) {
62
+ const nextStages = [...stages];
63
+ nextStages[index] = stage;
64
+ return ModularFactory(methods).build(nextStages)(displayName);
65
+ }
66
+ // Otherwise, append the stage as in multiple mode
67
+ return ModularFactory(methods).build([...stages, stage])(displayName);
68
+ });
69
+ });
70
+ return Component;
71
+ };
72
+ },
73
+ extend: (_methods) => {
74
+ return ModularFactory({
75
+ ...methods,
76
+ ..._methods,
77
+ });
78
+ },
79
+ };
80
+ }
81
+ export const modularFactory = ModularFactory({
82
+ withRender: {
83
+ field: 'render',
84
+ restrict: {},
85
+ },
86
+ });
87
+ export function createMethodRecord(record) {
88
+ return record;
89
+ }
90
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { FunctionComponent } from 'react';
2
+ declare type UnionToIntersection<U> = [U] extends [never] ? never : (U extends infer V ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
3
+ export declare type StageEntry = {
4
+ key: MethodName;
5
+ value: unknown;
6
+ stages: string;
7
+ };
8
+ declare type StageList = StageEntry[];
9
+ export declare type MethodName = `with${Capitalize<string>}`;
10
+ export declare type MethodEntry = {
11
+ field: string;
12
+ transform?: (args: any, value: any) => any;
13
+ restrict?: unknown;
14
+ multiple?: boolean;
15
+ empty?: boolean;
16
+ };
17
+ export declare type MethodRecord = Record<MethodName, MethodEntry>;
18
+ declare type AppendStage<List extends StageList, Stage extends MethodName, Value extends unknown, Prev extends StageEntry = GetStage<List, Stage>> = [
19
+ ...List,
20
+ {
21
+ key: Stage;
22
+ value: Value;
23
+ stages: [Prev] extends [never] ? List[number]['key'] : Prev['stages'];
24
+ }
25
+ ];
26
+ declare type DropStages<List extends StageList, Dropped extends string> = {
27
+ [Index in keyof List]: List[Index]['key'] extends Dropped ? never : List[Index];
28
+ };
29
+ declare type KeepStages<List extends StageList, Allowed extends string> = {
30
+ [Index in keyof List]: List[Index]['key'] extends Allowed ? List[Index] : never;
31
+ };
32
+ declare type GetStage<List extends StageList, Key extends MethodName, Union = List[number]> = Union extends {
33
+ key: Key;
34
+ value: infer U;
35
+ stages: infer S;
36
+ } ? {
37
+ key: Key;
38
+ value: U;
39
+ stages: S;
40
+ } : never;
41
+ declare type GetStageValue<List extends StageList, Key extends MethodName> = UnionToIntersection<GetStage<List, Key>['value']>;
42
+ declare type ComputeArgs<Stages extends StageList, Limit extends string, Union = Stages[number], Intersection = UnionToIntersection<Union extends {
43
+ key: infer Key;
44
+ value: infer Value;
45
+ } ? {
46
+ [key in Key extends string ? Key : never]: Value;
47
+ } : never>> = Pick<Intersection, Limit extends keyof Intersection ? Limit : never> extends infer U ? {
48
+ [key in keyof U]: U[key] extends Record<string, unknown> ? U[key] extends infer V ? {
49
+ [key in keyof V]: V[key];
50
+ } : never : U[key];
51
+ } : never;
52
+ declare type TransformArg<Value, Key> = Key extends keyof ModularStageTransform<Value> ? ModularStageTransform<Value>[Key] : Value;
53
+ declare type MapArgs<Stages extends StageList, Limit extends string, Methods extends MethodRecord, Props, Args = ComputeArgs<Stages, Limit>> = UnionToIntersection<{
54
+ props: Props;
55
+ } | {
56
+ [key in keyof Methods]: key extends keyof Args ? Methods[key] extends MethodEntry ? {
57
+ [k in Methods[key]['field']]: TransformArg<Args[key], key>;
58
+ } : never : never;
59
+ }[keyof Methods]> extends infer U ? {
60
+ [key in keyof U]: U[key] extends Record<string, unknown> ? U[key] extends infer V ? {
61
+ [key in keyof V]: V[key];
62
+ } : never : U[key];
63
+ } : never;
64
+ declare type ModularExtension<Props, Methods extends MethodRecord, Stages extends StageList> = {
65
+ [key in keyof Methods]: (Methods[key] extends MethodEntry ? Methods[key] : never)['restrict'] extends undefined ? <Key extends key extends MethodName ? key : never, Method extends Methods[Key] extends MethodEntry ? Methods[Key] : never, Prev extends Method['multiple'] extends true ? never : GetStageValue<Stages, Key>, Limit extends Method['multiple'] extends true ? Stages[number]['key'] : [GetStage<Stages, Key>] extends [never] ? Stages[number]['key'] : GetStage<Stages, Key>['stages'], KeptStages extends Method['multiple'] extends true ? DropStages<Stages, Key> : Stages, Value extends [Prev] extends [never] ? [Method['restrict']] extends [never] ? unknown : Method['restrict'] : Prev>(value?: Value | ((args: MapArgs<Stages, Limit, Methods, Props>) => Value | void)) => Modular<Props, Methods, AppendStage<KeptStages, Key, Value>> : <Key extends key extends MethodName ? key : never, Method extends Methods[Key] extends MethodEntry ? Methods[Key] : never, Prev extends Method['multiple'] extends true ? never : GetStageValue<Stages, Key>, Limit extends Method['multiple'] extends true ? Stages[number]['key'] : [GetStage<Stages, Key>] extends [never] ? Stages[number]['key'] : GetStage<Stages, Key>['stages'], KeptStages extends Method['multiple'] extends true ? DropStages<Stages, Key> : Stages, Value extends [Prev] extends [never] ? [Method['restrict']] extends [never] ? unknown : Method['restrict'] : Prev>(value: Value | ((args: MapArgs<Stages, Limit, Methods, Props>) => Value)) => Modular<Props, Methods, AppendStage<KeptStages, Key, Value>>;
66
+ } & {
67
+ atStage<Key extends Stages[number]['key'], KeptStages extends KeepStages<Stages, GetStage<Stages, Key>['stages'] | Key>>(stage: Key): Modular<Props, Methods, KeptStages>;
68
+ asHook(): Props extends {} ? () => MapArgs<Stages, Stages[number]['key'], Methods, Props> : (props: Props) => MapArgs<Stages, Stages[number]['key'], Methods, Props>;
69
+ asHook<Field extends keyof MapArgs<Stages, Stages[number]['key'], Methods, Props>>(field: Field): Props extends {} ? () => MapArgs<Stages, Stages[number]['key'], Methods, Props>[Field] : (props: Props) => MapArgs<Stages, Stages[number]['key'], Methods, Props>[Field];
70
+ };
71
+ export declare type Modular<Props, Methods extends MethodRecord, Stages extends StageList> = FunctionComponent<Props> & ModularExtension<Props, Methods, Stages>;
72
+ export interface ModularStageTransform<T> {
73
+ }
74
+ export {};
75
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@modular-component/core",
3
+ "description": "Delightfully organized and deeply testable React Components",
4
+ "keywords": [
5
+ "React",
6
+ "Modular",
7
+ "Factory",
8
+ "Test"
9
+ ],
10
+ "version": "0.1.0",
11
+ "type": "module",
12
+ "license": "MIT",
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "files": [
17
+ "src",
18
+ "dist",
19
+ "CHANGELOG.md"
20
+ ],
21
+ "scripts": {
22
+ "build": "yarn build:self",
23
+ "build:self": "tsc",
24
+ "license": "cp ../../LICENSE ./LICENSE"
25
+ },
26
+ "peerDependencies": {
27
+ "react": ">=17 <19"
28
+ },
29
+ "devDependencies": {
30
+ "@types/react": "^18.0.17",
31
+ "typescript": "^4.6.4"
32
+ },
33
+ "main": "dist/index.js",
34
+ "types": "dist/index.d.ts"
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,131 @@
1
+ import { MethodName, MethodRecord, Modular, StageEntry } from './types'
2
+ import { FunctionComponent } from 'react'
3
+
4
+ export type { Modular, ModularStageTransform, MethodRecord } from './types'
5
+
6
+ function ModularFactory<Methods extends MethodRecord>(methods: Methods) {
7
+ type CleanMethods = Methods extends infer U
8
+ ? { [key in keyof U]: U[key] }
9
+ : never
10
+
11
+ return {
12
+ build: <Stages extends StageEntry[] = []>(
13
+ stages: Omit<StageEntry, 'stages'>[] = [],
14
+ ) => {
15
+ return <Props = {}>(displayName?: string) => {
16
+ // Create the actual Component. This is a simple React Functional Component
17
+ // that will call the hooks for each registered stages in order.
18
+ const Component = ((props) => {
19
+ // Prepare the shared arguments object, prefilling it with the props
20
+ // and an empty render result
21
+ const useComponent = Component.asHook()
22
+ const args = useComponent(props)
23
+
24
+ return (args as unknown as { render: null }).render ?? null
25
+ }) as Modular<Props, CleanMethods, Stages>
26
+
27
+ // Set the debug display name if provided
28
+ Component.displayName = displayName
29
+
30
+ // Add an asHook system to get the components args as a reusable hook
31
+ Component.asHook = ((field: string) => (props: Props) => {
32
+ // Prepare the shared arguments object, prefilling it with the props
33
+ // and an empty render result
34
+ let args = { props, render: null }
35
+
36
+ // Run each stage in order, replacing the arguments by the response
37
+ // from the last stage
38
+ for (const stage of stages) {
39
+ const method = methods[stage.key as MethodName]
40
+ const useStage = stage.value
41
+ const useTransform =
42
+ method.transform ??
43
+ (() =>
44
+ typeof useStage === 'function'
45
+ ? useStage({ ...args })
46
+ : useStage)
47
+
48
+ args = {
49
+ ...args,
50
+ [method.field as keyof typeof args]: useTransform(args, useStage),
51
+ }
52
+ }
53
+
54
+ // Finally, return the args
55
+ return field ? args[field as keyof typeof args] : args
56
+ }) as unknown as Modular<Props, CleanMethods, Stages>['asHook']
57
+
58
+ // Add a function for rewinding the component up to a certain stage
59
+ Component.atStage = ((stage: MethodName) => {
60
+ // Find the needed stage
61
+ const stageIndex = stages.findIndex((record) => record.key === stage)
62
+
63
+ // If the stage cannot be found, create a brand new, empty component
64
+ if (stageIndex === -1) {
65
+ return ModularFactory<Methods>(methods).build()<Props>(displayName)
66
+ }
67
+
68
+ // Otherwise, keep all stages up to and including the found stage
69
+ return ModularFactory<Methods>(methods).build(
70
+ stages.slice(0, stageIndex + 1),
71
+ )<Props>(displayName)
72
+ }) as unknown as Modular<Props, CleanMethods, Stages>['atStage']
73
+
74
+ // Add each configured stage method to the component
75
+ Object.keys(methods).forEach((method) => {
76
+ Component[method as keyof CleanMethods] = ((value: unknown) => {
77
+ // Prepare the new stage
78
+ const stage = { key: method as MethodName, value }
79
+
80
+ // For stages in "multiple" mode, simply append the stage
81
+ if (methods[method as MethodName].multiple) {
82
+ return ModularFactory(methods).build([...stages, stage])<Props>(
83
+ displayName,
84
+ )
85
+ }
86
+
87
+ // For other stages, check if a stage of the same key already exists
88
+ const index = stages.findIndex((st) => st.key === method)
89
+
90
+ // If so, copy the stages and replace the previous record
91
+ if (index > -1) {
92
+ const nextStages = [...stages]
93
+ nextStages[index] = stage
94
+ return ModularFactory(methods).build(nextStages)<Props>(
95
+ displayName,
96
+ )
97
+ }
98
+
99
+ // Otherwise, append the stage as in multiple mode
100
+ return ModularFactory(methods).build([...stages, stage])<Props>(
101
+ displayName,
102
+ )
103
+ }) as unknown as Modular<
104
+ Props,
105
+ CleanMethods,
106
+ Stages
107
+ >[keyof CleanMethods]
108
+ })
109
+
110
+ return Component
111
+ }
112
+ },
113
+ extend: <_Methods extends MethodRecord>(_methods: _Methods) => {
114
+ return ModularFactory<Methods & _Methods>({
115
+ ...methods,
116
+ ..._methods,
117
+ })
118
+ },
119
+ }
120
+ }
121
+
122
+ export const modularFactory = ModularFactory({
123
+ withRender: {
124
+ field: 'render',
125
+ restrict: {} as ReturnType<FunctionComponent>,
126
+ },
127
+ } as const)
128
+
129
+ export function createMethodRecord<R extends MethodRecord>(record: R): R {
130
+ return record
131
+ }
package/src/types.ts ADDED
@@ -0,0 +1,272 @@
1
+ import { FunctionComponent } from 'react'
2
+
3
+ // Collapse a union of similar object into the intersection of the various objects
4
+ type UnionToIntersection<U> = [U] extends [never]
5
+ ? never
6
+ : (U extends infer V ? (k: U) => void : never) extends (k: infer I) => void
7
+ ? I
8
+ : never
9
+
10
+ // Base types used for manipulating stages
11
+ export type StageEntry = { key: MethodName; value: unknown; stages: string }
12
+ type StageList = StageEntry[]
13
+
14
+ // Base type used for manipulating methods
15
+ export type MethodName = `with${Capitalize<string>}`
16
+ export type MethodEntry = {
17
+ field: string
18
+ transform?: (args: any, value: any) => any
19
+ restrict?: unknown
20
+ multiple?: boolean
21
+ empty?: boolean
22
+ }
23
+ export type MethodRecord = Record<MethodName, MethodEntry>
24
+
25
+ // Take a stage list, and append a new stage at the end
26
+ type AppendStage<
27
+ List extends StageList,
28
+ Stage extends MethodName,
29
+ Value extends unknown,
30
+ Prev extends StageEntry = GetStage<List, Stage>,
31
+ > = [
32
+ ...List,
33
+ {
34
+ key: Stage
35
+ value: Value
36
+ stages: [Prev] extends [never] ? List[number]['key'] : Prev['stages']
37
+ },
38
+ ]
39
+
40
+ // Take a stage list, and remove all stages matching a set of keys
41
+ type DropStages<List extends StageList, Dropped extends string> = {
42
+ [Index in keyof List]: List[Index]['key'] extends Dropped
43
+ ? never
44
+ : List[Index]
45
+ }
46
+
47
+ // Take a stage list, and keep only the stages matching a set of keys
48
+ type KeepStages<List extends StageList, Allowed extends string> = {
49
+ [Index in keyof List]: List[Index]['key'] extends Allowed
50
+ ? List[Index]
51
+ : never
52
+ }
53
+
54
+ // Parse a stage list to extract all the stages matching a key
55
+ type GetStage<
56
+ List extends StageList,
57
+ Key extends MethodName,
58
+ Union = List[number],
59
+ > = Union extends { key: Key; value: infer U; stages: infer S }
60
+ ? { key: Key; value: U; stages: S }
61
+ : never
62
+
63
+ // Parse a stage list to extract the collapsed value for a given stage key
64
+ type GetStageValue<
65
+ List extends StageList,
66
+ Key extends MethodName,
67
+ > = UnionToIntersection<GetStage<List, Key>['value']>
68
+
69
+ // Collapse a complete stage list into an argument object
70
+ // Use the deep `extends infer U` method to get a clean object in tooltips
71
+ type ComputeArgs<
72
+ Stages extends StageList,
73
+ Limit extends string,
74
+ Union = Stages[number],
75
+ Intersection = UnionToIntersection<
76
+ Union extends { key: infer Key; value: infer Value }
77
+ ? { [key in Key extends string ? Key : never]: Value }
78
+ : never
79
+ >,
80
+ > = Pick<
81
+ Intersection,
82
+ Limit extends keyof Intersection ? Limit : never
83
+
84
+ // Deeply spread the object for cleaner type tooltips
85
+ > extends infer U
86
+ ? {
87
+ [key in keyof U]: U[key] extends Record<string, unknown>
88
+ ? U[key] extends infer V
89
+ ? { [key in keyof V]: V[key] }
90
+ : never
91
+ : U[key]
92
+ }
93
+ : never
94
+
95
+ // Check if a custom transform exists for the given argument, and apply
96
+ // it to the current value if there is
97
+ type TransformArg<Value, Key> = Key extends keyof ModularStageTransform<Value>
98
+ ? ModularStageTransform<Value>[Key]
99
+ : Value
100
+
101
+ // Map all computed arguments against the methods map to convert
102
+ // from the stage key to the wanted field name
103
+ // Use the deep `extends infer U` method to get a clean object in tooltips
104
+ type MapArgs<
105
+ Stages extends StageList,
106
+ Limit extends string,
107
+ Methods extends MethodRecord,
108
+ Props,
109
+ Args = ComputeArgs<Stages, Limit>,
110
+ > = UnionToIntersection<
111
+ // Start by injecting the original props
112
+ | { props: Props }
113
+ // Map over all configured methods
114
+ | {
115
+ [key in keyof Methods]: key extends keyof Args // Check if an arg exists for the given method
116
+ ? Methods[key] extends MethodEntry
117
+ ? {
118
+ // Extract the field name from the method
119
+ [k in Methods[key]['field']]: TransformArg<Args[key], key>
120
+ }
121
+ : never
122
+ : // Set to never if the arg does not exist
123
+ never
124
+ }[keyof Methods]
125
+
126
+ // Deeply spread the object for cleaner type tooltips
127
+ > extends infer U
128
+ ? {
129
+ [key in keyof U]: U[key] extends Record<string, unknown>
130
+ ? U[key] extends infer V
131
+ ? { [key in keyof V]: V[key] }
132
+ : never
133
+ : U[key]
134
+ }
135
+ : never
136
+
137
+ type ModularExtension<
138
+ Props,
139
+ Methods extends MethodRecord,
140
+ Stages extends StageList,
141
+ > = {
142
+ [key in keyof Methods]: (Methods[key] extends MethodEntry
143
+ ? Methods[key]
144
+ : never)['restrict'] extends undefined
145
+ ? <
146
+ // Cast the method key to a method name
147
+ Key extends key extends MethodName ? key : never,
148
+ // Extract the current method
149
+ Method extends Methods[Key] extends MethodEntry ? Methods[Key] : never,
150
+ // If the mode is not 'multiple', get any previous value used
151
+ // for the stage
152
+ Prev extends Method['multiple'] extends true
153
+ ? never
154
+ : GetStageValue<Stages, Key>,
155
+ // Find the stages occurring before the first instance of the current
156
+ // stage, in order to limit the arguments to those defined before the
157
+ // stage. This is needed for 'single' mode stages that are called
158
+ // multiple time.
159
+ Limit extends Method['multiple'] extends true // Ignore for 'multiple' stages
160
+ ? Stages[number]['key']
161
+ : [GetStage<Stages, Key>] extends [never] // Ignore if it's the first time we see the stage
162
+ ? Stages[number]['key']
163
+ : GetStage<Stages, Key>['stages'],
164
+ // Compute the kept stages by dropping all references to current stage
165
+ // for multiple mode, or keeping all previous stages for single mode
166
+ KeptStages extends Method['multiple'] extends true
167
+ ? DropStages<Stages, Key>
168
+ : Stages,
169
+ // Get the value to use or infer, restricting it to any
170
+ // configured restriction or previously used values
171
+ Value extends [Prev] extends [never]
172
+ ? [Method['restrict']] extends [never]
173
+ ? unknown
174
+ : Method['restrict']
175
+ : Prev,
176
+ >(
177
+ // A stage accepts either a direct value or a function (hook) generating the value
178
+ value?:
179
+ | Value
180
+ | ((args: MapArgs<Stages, Limit, Methods, Props>) => Value | void),
181
+ ) => Modular<Props, Methods, AppendStage<KeptStages, Key, Value>>
182
+ : <
183
+ // Cast the method key to a method name
184
+ Key extends key extends MethodName ? key : never,
185
+ // Extract the current method
186
+ Method extends Methods[Key] extends MethodEntry ? Methods[Key] : never,
187
+ // If the mode is not 'multiple', get any previous value used
188
+ // for the stage
189
+ Prev extends Method['multiple'] extends true
190
+ ? never
191
+ : GetStageValue<Stages, Key>,
192
+ // Find the stages occurring before the first instance of the current
193
+ // stage, in order to limit the arguments to those defined before the
194
+ // stage. This is needed for 'single' mode stages that are called
195
+ // multiple time.
196
+ Limit extends Method['multiple'] extends true // Ignore for 'multiple' stages
197
+ ? Stages[number]['key']
198
+ : [GetStage<Stages, Key>] extends [never] // Ignore if it's the first time we see the stage
199
+ ? Stages[number]['key']
200
+ : GetStage<Stages, Key>['stages'],
201
+ // Compute the kept stages by dropping all references to current stage
202
+ // for multiple mode, or keeping all previous stages for single mode
203
+ KeptStages extends Method['multiple'] extends true
204
+ ? DropStages<Stages, Key>
205
+ : Stages,
206
+ // Get the value to use or infer, restricting it to any
207
+ // configured restriction or previously used values
208
+ Value extends [Prev] extends [never]
209
+ ? [Method['restrict']] extends [never]
210
+ ? unknown
211
+ : Method['restrict']
212
+ : Prev,
213
+ >(
214
+ // A stage accepts either a direct value or a function (hook) generating the value
215
+ value:
216
+ | Value
217
+ | ((args: MapArgs<Stages, Limit, Methods, Props>) => Value),
218
+ ) => Modular<Props, Methods, AppendStage<KeptStages, Key, Value>>
219
+ } & {
220
+ // Create a new component using the same stages as the current component
221
+ // up to a certain point
222
+ atStage<
223
+ // Key of the stage to keep to
224
+ Key extends Stages[number]['key'],
225
+ // List of stages up to the one referenced by the key
226
+ KeptStages extends KeepStages<
227
+ Stages,
228
+ GetStage<Stages, Key>['stages'] | Key
229
+ >,
230
+ >(
231
+ stage: Key,
232
+ ): Modular<Props, Methods, KeptStages>
233
+ // Generate a hook instead of a component, returning the generated arguments
234
+ asHook(): Props extends {}
235
+ ? () => MapArgs<Stages, Stages[number]['key'], Methods, Props>
236
+ : (props: Props) => MapArgs<Stages, Stages[number]['key'], Methods, Props>
237
+ // Overload the asHook function to allow taking in a field from the generated arguments,
238
+ // and returning only the value from this field
239
+ asHook<
240
+ Field extends keyof MapArgs<Stages, Stages[number]['key'], Methods, Props>,
241
+ >(
242
+ field: Field,
243
+ ): Props extends {}
244
+ ? () => MapArgs<Stages, Stages[number]['key'], Methods, Props>[Field]
245
+ : (
246
+ props: Props,
247
+ ) => MapArgs<Stages, Stages[number]['key'], Methods, Props>[Field]
248
+ }
249
+
250
+ // Type used for describing a ModularComponent. In essence, it is a FunctionComponent
251
+ // with all the factories methods appended.
252
+ // In this type, we strongly-type all factory methods and the enhanced returned component
253
+ // by keeping track of all the previous stages through a stage-list tuple
254
+ export type Modular<
255
+ // The props are set and are invariant for the component, but the `props` argument
256
+ // itself can be extended
257
+ Props,
258
+ // List of all methods available for the component, won't change as the component
259
+ // is built but depends on the factory that started the build
260
+ Methods extends MethodRecord,
261
+ // Finally, the list of stages that will be appended as methods are called
262
+ Stages extends StageList,
263
+ > =
264
+ // At its core, the ModularComponent is a normal FunctionComponent taking
265
+ // the original props, extended with additional methods
266
+ FunctionComponent<Props> & ModularExtension<Props, Methods, Stages>
267
+
268
+ /* Exposed interfaces used for extending functionality */
269
+
270
+ // Add a value transformation for a given stage. The T type is the
271
+ // original type of the value passed to the stage.
272
+ export interface ModularStageTransform<T> {}