@modular-component/core 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/index.d.ts +304 -91
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +107 -53
  5. package/dist/index.js.map +1 -1
  6. package/dist/types/arguments.d.ts +33 -0
  7. package/dist/types/arguments.d.ts.map +1 -0
  8. package/dist/types/arguments.js +6 -0
  9. package/dist/types/arguments.js.map +1 -0
  10. package/dist/types/methods/add.d.ts +23 -0
  11. package/dist/types/methods/add.d.ts.map +1 -0
  12. package/dist/types/methods/add.js +8 -0
  13. package/dist/types/methods/add.js.map +1 -0
  14. package/dist/types/methods/at.d.ts +22 -0
  15. package/dist/types/methods/at.d.ts.map +1 -0
  16. package/dist/types/methods/at.js +7 -0
  17. package/dist/types/methods/at.js.map +1 -0
  18. package/dist/types/methods/hook.d.ts +14 -0
  19. package/dist/types/methods/hook.d.ts.map +1 -0
  20. package/dist/types/methods/hook.js +7 -0
  21. package/dist/types/methods/hook.js.map +1 -0
  22. package/dist/types/methods/mock.d.ts +26 -0
  23. package/dist/types/methods/mock.d.ts.map +1 -0
  24. package/dist/types/methods/mock.js +6 -0
  25. package/dist/types/methods/mock.js.map +1 -0
  26. package/dist/types/methods/with.d.ts +44 -0
  27. package/dist/types/methods/with.d.ts.map +1 -0
  28. package/dist/types/methods/with.js +8 -0
  29. package/dist/types/methods/with.js.map +1 -0
  30. package/dist/types/methods.d.ts +10 -0
  31. package/dist/types/methods.d.ts.map +1 -0
  32. package/dist/types/methods.js +5 -0
  33. package/dist/types/methods.js.map +1 -0
  34. package/dist/types/modular-component.d.ts +15 -0
  35. package/dist/types/modular-component.d.ts.map +1 -0
  36. package/dist/types/modular-component.js +5 -0
  37. package/dist/types/modular-component.js.map +1 -0
  38. package/dist/types/stage.d.ts +41 -0
  39. package/dist/types/stage.d.ts.map +1 -0
  40. package/dist/types/stage.js +5 -0
  41. package/dist/types/stage.js.map +1 -0
  42. package/dist/types/utils.d.ts +24 -0
  43. package/dist/types/utils.d.ts.map +1 -0
  44. package/dist/types/utils.js +5 -0
  45. package/dist/types/utils.js.map +1 -0
  46. package/dist/types/validation.d.ts +4 -0
  47. package/dist/types/validation.d.ts.map +1 -0
  48. package/dist/types/validation.js +2 -0
  49. package/dist/types/validation.js.map +1 -0
  50. package/package.json +1 -1
  51. package/src/index.ts +185 -83
  52. package/src/types/arguments.ts +55 -0
  53. package/src/types/methods/add.ts +50 -0
  54. package/src/types/methods/at.ts +64 -0
  55. package/src/types/methods/hook.ts +27 -0
  56. package/src/types/methods/mock.ts +90 -0
  57. package/src/types/methods/with.ts +153 -0
  58. package/src/types/methods.ts +11 -0
  59. package/src/types/modular-component.ts +25 -0
  60. package/src/types/stage.ts +91 -0
  61. package/src/types/utils.ts +63 -0
  62. package/src/types/validation.ts +13 -0
  63. package/dist/types.d.ts +0 -79
  64. package/dist/types.d.ts.map +0 -1
  65. package/dist/types.js +0 -2
  66. package/dist/types.js.map +0 -1
  67. package/src/types.ts +0 -279
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Helper for manipulating stages and stage tuples
3
+ */
4
+ import { At, Before, Reduce, After, OneLess } from './utils';
5
+ export interface ModularStages<Args = any, Value = any> {
6
+ }
7
+ declare type StageRecord = {
8
+ stage: keyof ModularStages;
9
+ value: unknown;
10
+ };
11
+ export declare type StageTuple = StageRecord[];
12
+ export declare type AddStage<List extends StageTuple, Stage extends StageRecord> = [
13
+ ...List,
14
+ Stage
15
+ ];
16
+ export declare type ReplaceStageAt<List extends StageTuple, Stage extends StageRecord, At extends number | string> = {
17
+ [Index in keyof List]: Index extends At ? List[Index]['stage'] extends Stage['stage'] ? Stage : List[Index] : List[Index];
18
+ };
19
+ declare type ReplaceStage<List extends StageTuple, Stage extends StageRecord, At extends number | string = never> = [At] extends [never] ? {
20
+ [Index in keyof List]: List[Index]['stage'] extends Stage['stage'] ? Stage : List[Index];
21
+ } : ReplaceStageAt<List, Stage, At>;
22
+ export declare type ExtractStage<List extends StageTuple, Stage extends symbol, Union = List[number]> = Union extends {
23
+ stage: Stage;
24
+ value: infer U;
25
+ } ? {
26
+ stage: Stage;
27
+ value: U;
28
+ } : never;
29
+ export declare type UpsertStage<List extends StageTuple, Stage extends StageRecord, At extends number = never> = [ExtractStage<List, Stage['stage']>] extends [never] ? AddStage<List, Stage> : ReplaceStage<List, Stage, At>;
30
+ declare type IsolateStage<Tuple extends StageTuple, Stage extends symbol> = {
31
+ [key in keyof Tuple]: Tuple[key]['stage'] extends Stage ? key : never;
32
+ };
33
+ export declare type StageIndices<Tuple extends StageTuple, Stage extends symbol> = Reduce<IsolateStage<Tuple, Stage>>;
34
+ export declare type AtStage<Tuple extends StageTuple, Index extends number | string> = At<Tuple, Index> extends StageTuple ? At<Tuple, Index> : [];
35
+ export declare type BeforeStage<Tuple extends StageTuple, Index extends number | string> = Before<Tuple, Index> extends StageTuple ? Before<Tuple, Index> : [];
36
+ declare type AfterStage<Tuple extends StageTuple, Index extends number | string> = After<Tuple, Index> extends StageTuple ? After<Tuple, Index> : [];
37
+ export declare type CleanUpStages<Tuple extends StageTuple> = {
38
+ [key in keyof Tuple]: StageIndices<OneLess<AfterStage<Tuple, key>>, Tuple[key]['stage']>['length'] extends 0 ? Tuple[key] : never;
39
+ };
40
+ export {};
41
+ //# sourceMappingURL=stage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../src/types/stage.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAE5D,MAAM,WAAW,aAAa,CAAC,IAAI,GAAG,GAAG,EAAE,KAAK,GAAG,GAAG;CAAI;AAE1D,aAAK,WAAW,GAAG;IACjB,KAAK,EAAE,MAAM,aAAa,CAAA;IAC1B,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AACD,oBAAY,UAAU,GAAG,WAAW,EAAE,CAAA;AAEtC,oBAAY,QAAQ,CAAC,IAAI,SAAS,UAAU,EAAE,KAAK,SAAS,WAAW,IAAI;IACzE,GAAG,IAAI;IACP,KAAK;CACN,CAAA;AAED,oBAAY,cAAc,CACxB,IAAI,SAAS,UAAU,EACvB,KAAK,SAAS,WAAW,EACzB,EAAE,SAAS,MAAM,GAAG,MAAM,IACxB;KACD,KAAK,IAAI,MAAM,IAAI,GAAG,KAAK,SAAS,EAAE,GACnC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,KAAK,CAAC,OAAO,CAAC,GACzC,KAAK,GACL,IAAI,CAAC,KAAK,CAAC,GACb,IAAI,CAAC,KAAK,CAAC;CAChB,CAAA;AAED,aAAK,YAAY,CACf,IAAI,SAAS,UAAU,EACvB,KAAK,SAAS,WAAW,EACzB,EAAE,SAAS,MAAM,GAAG,MAAM,GAAG,KAAK,IAChC,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,GACpB;KACG,KAAK,IAAI,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,KAAK,CAAC,OAAO,CAAC,GAC9D,KAAK,GACL,IAAI,CAAC,KAAK,CAAC;CAChB,GACD,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;AAEnC,oBAAY,YAAY,CACtB,IAAI,SAAS,UAAU,EACvB,KAAK,SAAS,MAAM,EACpB,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAClB,KAAK,SAAS;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,CAAA;CAAE,GAC9C;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC1B,KAAK,CAAA;AAET,oBAAY,WAAW,CACrB,IAAI,SAAS,UAAU,EACvB,KAAK,SAAS,WAAW,EACzB,EAAE,SAAS,MAAM,GAAG,KAAK,IACvB,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACpD,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,GACrB,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;AAEjC,aAAK,YAAY,CAAC,KAAK,SAAS,UAAU,EAAE,KAAK,SAAS,MAAM,IAAI;KACjE,GAAG,IAAI,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,KAAK,GAAG,GAAG,GAAG,KAAK;CACtE,CAAA;AAED,oBAAY,YAAY,CACtB,KAAK,SAAS,UAAU,EACxB,KAAK,SAAS,MAAM,IAClB,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;AAEtC,oBAAY,OAAO,CACjB,KAAK,SAAS,UAAU,EACxB,KAAK,SAAS,MAAM,GAAG,MAAM,IAC3B,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,UAAU,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAA;AAE/D,oBAAY,WAAW,CACrB,KAAK,SAAS,UAAU,EACxB,KAAK,SAAS,MAAM,GAAG,MAAM,IAC3B,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAA;AAEvE,aAAK,UAAU,CACb,KAAK,SAAS,UAAU,EACxB,KAAK,SAAS,MAAM,GAAG,MAAM,IAC3B,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,UAAU,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAA;AAErE,oBAAY,aAAa,CAAC,KAAK,SAAS,UAAU,IAAI;KACnD,GAAG,IAAI,MAAM,KAAK,GAAG,YAAY,CAChC,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,EAC/B,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CACpB,CAAC,QAAQ,CAAC,SAAS,CAAC,GACjB,KAAK,CAAC,GAAG,CAAC,GACV,KAAK;CACV,CAAA"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Helper for manipulating stages and stage tuples
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=stage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage.js","sourceRoot":"","sources":["../../src/types/stage.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Generic type helpers (tuple, union, object...)
3
+ */
4
+ export declare type OneLess<Tuple extends unknown[]> = Tuple extends [
5
+ unknown,
6
+ ...infer Rest
7
+ ] ? Rest : Tuple;
8
+ export declare type Before<Tuple extends unknown[], Index extends string | number> = `${Tuple['length']}` extends `${Index}` ? Tuple : Tuple extends [...infer Keep, infer Drop] ? Before<Keep, Index> : never;
9
+ export declare type After<Tuple extends unknown[], Index extends string | number, Rest extends unknown[] = []> = `${Rest['length']}` extends `${Index}` ? Tuple : Tuple extends [infer Drop, ...infer Keep] ? After<Keep, Index, [...Rest, Drop]> : never;
10
+ export declare type At<Tuple extends unknown[], Index extends string | number> = `${OneLess<Tuple>['length']}` extends `${Index}` ? Tuple : Tuple extends [...infer Keep, infer Drop] ? At<Keep, Index> : never;
11
+ export declare type Reduce<T extends unknown[]> = T extends [...infer R, infer L] ? [L] extends [never] ? [...Reduce<R>] : [...Reduce<R>, L] : T;
12
+ export declare type ToIndices<T extends unknown[]> = {
13
+ [key in keyof T]: key;
14
+ }[number];
15
+ export declare type Last<T extends unknown[]> = T extends [...infer R, infer L] ? L : never;
16
+ export declare type UnionToIntersection<U> = [U] extends [never] ? never : (U extends infer V ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
17
+ declare type NonNeverKeys<Obj extends {}> = {
18
+ [key in keyof Obj]: Obj[key] extends never ? never : key;
19
+ }[keyof Obj];
20
+ export declare type FilterNever<Obj extends {}> = {
21
+ [key in NonNeverKeys<Obj>]: Obj[key];
22
+ };
23
+ export {};
24
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/types/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,oBAAY,OAAO,CAAC,KAAK,SAAS,OAAO,EAAE,IAAI,KAAK,SAAS;IAC3D,OAAO;IACP,GAAG,MAAM,IAAI;CACd,GACG,IAAI,GACJ,KAAK,CAAA;AACT,oBAAY,MAAM,CAChB,KAAK,SAAS,OAAO,EAAE,EACvB,KAAK,SAAS,MAAM,GAAG,MAAM,IAC3B,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK,EAAE,GACvC,KAAK,GACL,KAAK,SAAS,CAAC,GAAG,MAAM,IAAI,EAAE,MAAM,IAAI,CAAC,GACzC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GACnB,KAAK,CAAA;AACT,oBAAY,KAAK,CACf,KAAK,SAAS,OAAO,EAAE,EACvB,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,IAAI,SAAS,OAAO,EAAE,GAAG,EAAE,IACzB,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK,EAAE,GACtC,KAAK,GACL,KAAK,SAAS,CAAC,MAAM,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,GACzC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,GACnC,KAAK,CAAA;AACT,oBAAY,EAAE,CACZ,KAAK,SAAS,OAAO,EAAE,EACvB,KAAK,SAAS,MAAM,GAAG,MAAM,IAC3B,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK,EAAE,GAChD,KAAK,GACL,KAAK,SAAS,CAAC,GAAG,MAAM,IAAI,EAAE,MAAM,IAAI,CAAC,GACzC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,GACf,KAAK,CAAA;AACT,oBAAY,MAAM,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GACrE,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACjB,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,GACd,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GACnB,CAAC,CAAA;AACL,oBAAY,SAAS,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI;KAAG,GAAG,IAAI,MAAM,CAAC,GAAG,GAAG;CAAE,CAAC,MAAM,CAAC,CAAA;AAC9E,oBAAY,IAAI,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GACnE,CAAC,GACD,KAAK,CAAA;AAKT,oBAAY,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACpD,KAAK,GACL,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,IAAI,GACzE,CAAC,GACD,KAAK,CAAA;AAGT,aAAK,YAAY,CAAC,GAAG,SAAS,EAAE,IAAI;KACjC,GAAG,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,GAAG;CACzD,CAAC,MAAM,GAAG,CAAC,CAAA;AAEZ,oBAAY,WAAW,CAAC,GAAG,SAAS,EAAE,IAAI;KACvC,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;CACrC,CAAA"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generic type helpers (tuple, union, object...)
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/types/utils.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,4 @@
1
+ import { ModularStages } from './stage';
2
+ export declare type ValidateIndex<Index extends number | undefined, Candidates extends string | number> = `${Index}` extends Candidates ? true : false;
3
+ export declare type RestrictValue<Arguments extends {}, Stage extends keyof ModularStages> = ModularStages<Arguments>[Stage]['restrict'] extends never ? unknown : ModularStages<Arguments>[Stage]['restrict'];
4
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/types/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAEvC,oBAAY,aAAa,CACvB,KAAK,SAAS,MAAM,GAAG,SAAS,EAChC,UAAU,SAAS,MAAM,GAAG,MAAM,IAChC,GAAG,KAAK,EAAE,SAAS,UAAU,GAAG,IAAI,GAAG,KAAK,CAAA;AAEhD,oBAAY,aAAa,CACvB,SAAS,SAAS,EAAE,EACpB,KAAK,SAAS,MAAM,aAAa,IAC/B,aAAa,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,SAAS,KAAK,GACzD,OAAO,GACP,aAAa,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/types/validation.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "Factory",
8
8
  "Test"
9
9
  ],
10
- "version": "0.1.3",
10
+ "version": "0.1.4",
11
11
  "type": "module",
12
12
  "license": "MIT",
13
13
  "publishConfig": {
package/src/index.ts CHANGED
@@ -1,122 +1,208 @@
1
- import { MethodName, MethodRecord, Modular, StageEntry } from './types'
2
- import { FunctionComponent } from 'react'
1
+ import { ForwardedRef, FunctionComponent, forwardRef, memo } from 'react'
3
2
 
4
- export type { Modular, ModularStageTransform, MethodRecord } from './types'
3
+ import { ModularComponent } from './types/modular-component'
4
+ import { MethodRecord } from './types/methods'
5
+ import { StageTuple } from './types/stage'
5
6
 
6
- function ModularFactory<Methods extends MethodRecord>(methods: Methods) {
7
+ export type { ModularStages } from './types/stage'
8
+
9
+ function ModularFactory<Methods extends Record<string, MethodRecord>>(
10
+ methods: Methods,
11
+ ) {
7
12
  type CleanMethods = Methods extends infer U
8
13
  ? { [key in keyof U]: U[key] }
9
14
  : never
10
15
 
11
16
  return {
12
- build: <Stages extends StageEntry[] = []>(
13
- stages: Omit<StageEntry, 'stages'>[] = [],
14
- ) => {
15
- return <Props = {}>(displayName?: string) => {
17
+ build: <Stages extends StageTuple = []>(stages: StageTuple = []) => {
18
+ const factory = <Props extends {} = {}, Ref = FunctionComponent<Props>>(
19
+ displayName?: string,
20
+ options?: { memo: boolean },
21
+ ) => {
22
+ const generateAsHook =
23
+ (ref?: ForwardedRef<Ref>, field?: string) =>
24
+ () =>
25
+ (props: Props = {} as Props) => {
26
+ // Prepare the shared arguments object, prefilling it with the props
27
+ // and an empty render result
28
+ let args = {
29
+ props,
30
+ children: (props as { children?: unknown })?.children,
31
+ render: null,
32
+ ref,
33
+ }
34
+
35
+ const methodsArray = Object.values(methods)
36
+
37
+ // Run each stage in order, replacing the arguments by the response
38
+ // from the last stage
39
+ for (const stage of stages) {
40
+ const method = methodsArray.find(
41
+ (method) => method.symbol === stage.stage,
42
+ )
43
+
44
+ let useTransform =
45
+ // Never transform mocked stages
46
+ ((stage as any).mocked ? undefined : method?.transform) ??
47
+ (() => stage.value)
48
+
49
+ args = {
50
+ ...args,
51
+ [method?.field as keyof typeof args]: useTransform(
52
+ args,
53
+ stage.value,
54
+ ),
55
+ }
56
+ }
57
+
58
+ // Finally, return the args
59
+ return field ? args[field as keyof typeof args] : args
60
+ }
61
+
16
62
  // Create the actual Component. This is a simple React Functional Component
17
63
  // that will call the hooks for each registered stages in order.
18
- const Component = ((props) => {
64
+ const Component = forwardRef<Ref, Props>((props, ref) => {
19
65
  // Prepare the shared arguments object, prefilling it with the props
20
66
  // and an empty render result
21
- const useComponent = Component.asHook()
67
+ const useComponent = generateAsHook(ref)()
22
68
  const args = useComponent(props)
23
69
 
24
70
  return (args as unknown as { render: null }).render ?? null
25
- }) as Modular<Props, CleanMethods, Stages>
71
+ }) as unknown as ModularComponent<Props, Ref, CleanMethods, Stages>
26
72
 
27
73
  // Set the debug display name if provided
28
74
  Component.displayName = displayName
29
75
 
30
76
  // 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, children: (props as { children?: unknown }).children, 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
77
+ Component.asHook = generateAsHook() as ModularComponent<
78
+ Props,
79
+ Ref,
80
+ CleanMethods,
81
+ Stages
82
+ >['asHook']
83
+
84
+ // Add each configured stage methods to the component
85
+ Object.keys(methods).forEach((method) => {
86
+ // Check if a stage of the same key already exists
87
+ const stageIndices = stages
62
88
  // Map all stages to an [index, stage] tuple
63
89
  .map((record, index) => [index, record] as const)
64
90
  // Remove all tuples not matching our stage
65
- .filter(([, record]) => record.key === stage)
66
- // Get the index of the very last one, or -1 if none are remaining
67
- .pop() || [-1])[0]
68
-
69
- // If the stage cannot be found, create a brand new, empty component
70
- if (stageIndex === -1) {
71
- return ModularFactory<Methods>(methods).build()<Props>(displayName)
72
- }
73
-
74
- // Otherwise, keep all stages up to and including the found stage
75
- return ModularFactory<Methods>(methods).build(
76
- stages.slice(0, stageIndex + 1),
77
- )<Props>(displayName)
78
- }) as unknown as Modular<Props, CleanMethods, Stages>['atStage']
79
-
80
- // Add each configured stage method to the component
81
- Object.keys(methods).forEach((method) => {
82
- Component[method as keyof CleanMethods] = ((value: unknown) => {
91
+ .filter(([, record]) => record.stage === methods[method].symbol)
92
+ // Get the index
93
+ .map(([index]) => index)
94
+ const lastIndex = [...stageIndices].pop() as number
95
+
96
+ // @ts-ignore
97
+ Component[`with${method}`] = (
98
+ value: unknown,
99
+ forceIndex?: number,
100
+ ) => {
83
101
  // Prepare the new stage
84
- const stage = { key: method as MethodName, value }
85
-
86
- // For stages in "multiple" mode, simply append the stage
87
- if (methods[method as MethodName].multiple) {
88
- return ModularFactory(methods).build([...stages, stage])<Props>(
89
- displayName,
90
- )
91
- }
102
+ const stage = { stage: methods[method].symbol, value } as const
92
103
 
93
- // For other stages, check if a stage of the same key already exists
94
- const index = stages.findIndex((st) => st.key === method)
104
+ // Check if a stage of the same key already exists
105
+ const stageIndex =
106
+ (forceIndex !== undefined
107
+ ? stageIndices[forceIndex]
108
+ : lastIndex) ?? -1
95
109
 
96
110
  // If so, copy the stages and replace the previous record
97
- if (index > -1) {
111
+ if (stageIndex > -1) {
98
112
  const nextStages = [...stages]
99
- nextStages[index] = stage
113
+ nextStages[stageIndex] = stage
100
114
  return ModularFactory(methods).build(nextStages)<Props>(
101
115
  displayName,
102
116
  )
103
117
  }
104
118
 
105
- // Otherwise, append the stage as in multiple mode
119
+ // Otherwise, append the stage
106
120
  return ModularFactory(methods).build([...stages, stage])<Props>(
107
121
  displayName,
108
122
  )
109
- }) as unknown as Modular<
110
- Props,
111
- CleanMethods,
112
- Stages
113
- >[keyof CleanMethods]
123
+ }
124
+
125
+ // @ts-ignore
126
+ Component[`add${method}`] =
127
+ stageIndices.length < 1
128
+ ? undefined
129
+ : (value: unknown) => {
130
+ // Prepare the new stage
131
+ const stage = {
132
+ stage: methods[method].symbol,
133
+ value,
134
+ } as const
135
+
136
+ // Append the stage as in multiple mode
137
+ return ModularFactory(methods).build([
138
+ ...stages,
139
+ stage,
140
+ ])<Props>(displayName)
141
+ }
142
+
143
+ // @ts-ignore
144
+ Component[`at${method}`] =
145
+ stageIndices.length < 1
146
+ ? undefined
147
+ : (forceIndex?: number) => {
148
+ // Find the needed stage
149
+ const stageIndex =
150
+ (forceIndex !== undefined
151
+ ? stageIndices[forceIndex]
152
+ : lastIndex) ?? lastIndex
153
+
154
+ // Otherwise, keep all stages up to and including the found stage
155
+ return ModularFactory<Methods>(methods).build(
156
+ stages.slice(0, stageIndex + 1),
157
+ )<Props>(displayName)
158
+ }
159
+
160
+ // @ts-ignore
161
+ Component[`mock${method}`] =
162
+ stageIndices.length < 1
163
+ ? undefined
164
+ : (value: unknown, forceIndex?: number) => {
165
+ // Prepare the mocked stage
166
+ const stage = {
167
+ stage: methods[method].symbol,
168
+ value,
169
+ mocked: true,
170
+ } as const
171
+
172
+ // Find the needed stage
173
+ const stageIndex =
174
+ (forceIndex !== undefined
175
+ ? stageIndices[forceIndex]
176
+ : lastIndex) ?? lastIndex
177
+
178
+ // Replace the stage with its mock
179
+ const nextStages = [...stages]
180
+ nextStages[stageIndex] = stage
181
+ return ModularFactory(methods).build(nextStages)<Props>(
182
+ displayName,
183
+ )
184
+ }
185
+
186
+ const capitalize = (str: string) =>
187
+ str[0].toUpperCase() + str.slice(1)
188
+
189
+ // @ts-ignore
190
+ Component[`asUse${capitalize(methods[method].field)}`] =
191
+ generateAsHook(undefined, methods[method].field)
114
192
  })
115
193
 
116
- return Component
194
+ return (options?.memo ? memo(Component) : Component) as typeof Component
117
195
  }
196
+
197
+ factory.memo = <Props extends {} = {}, Ref = FunctionComponent<Props>>(
198
+ displayName?: string,
199
+ ) => factory<Props, Ref>(displayName, { memo: true })
200
+
201
+ return factory
118
202
  },
119
- extend: <_Methods extends MethodRecord>(_methods: _Methods) => {
203
+ extend: <_Methods extends Record<string, MethodRecord>>(
204
+ _methods: _Methods,
205
+ ) => {
120
206
  return ModularFactory<Methods & _Methods>({
121
207
  ...methods,
122
208
  ..._methods,
@@ -125,13 +211,29 @@ function ModularFactory<Methods extends MethodRecord>(methods: Methods) {
125
211
  }
126
212
  }
127
213
 
214
+ const withRender = Symbol()
215
+
216
+ declare module './types/stage' {
217
+ export interface ModularStages<Args, Value> {
218
+ [withRender]: {
219
+ restrict: FunctionComponent<Args>
220
+ transform: ReturnType<
221
+ Value extends FunctionComponent<Args> ? Value : never
222
+ >
223
+ }
224
+ }
225
+ }
226
+
128
227
  export const modularFactory = ModularFactory({
129
- withRender: {
228
+ Render: {
229
+ symbol: withRender,
130
230
  field: 'render',
131
- restrict: {} as ReturnType<FunctionComponent>,
231
+ transform: (args, useStage) => useStage(args),
132
232
  },
133
233
  } as const)
134
234
 
135
- export function createMethodRecord<R extends MethodRecord>(record: R): R {
235
+ export function createMethodRecord<R extends Record<string, MethodRecord>>(
236
+ record: R,
237
+ ): R {
136
238
  return record
137
239
  }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Helpers for computing the arguments map generated by a given
3
+ * set of stages
4
+ */
5
+
6
+ import { PropsWithChildren } from 'react'
7
+ import { StageTuple, ModularStages, BeforeStage, CleanUpStages } from './stage'
8
+ import { UnionToIntersection } from './utils'
9
+
10
+ export type ComputeArguments<
11
+ Props extends {},
12
+ Ref,
13
+ Methods extends Record<
14
+ string,
15
+ { symbol: keyof ModularStages; field: string }
16
+ >,
17
+ Stages extends StageTuple,
18
+ CleanedStages extends StageTuple = CleanUpStages<Stages>,
19
+ MethodList = Methods[keyof Methods],
20
+ > = {
21
+ props: Props extends { children: unknown } ? Props : PropsWithChildren<Props>
22
+ ref: Ref
23
+ children: Props extends { children: infer C } ? C : PropsWithChildren['children']
24
+ } & (Stages['length'] extends 0
25
+ ? {}
26
+ : UnionToIntersection<
27
+ {
28
+ [key in keyof CleanedStages]: MethodList extends {
29
+ symbol: CleanedStages[key]['stage']
30
+ field: infer F
31
+ }
32
+ ? {
33
+ [k in F extends string ? F : never]: ModularStages<
34
+ ComputeArguments<
35
+ Props,
36
+ Ref,
37
+ Methods,
38
+ BeforeStage<CleanedStages, key>
39
+ >,
40
+ CleanedStages[key]['value']
41
+ >[CleanedStages[key]['stage']] extends { transform: infer T }
42
+ ? T
43
+ : CleanedStages[key]['value']
44
+ }
45
+ : never
46
+ }[number]
47
+ >) extends infer U
48
+ ? {
49
+ [key in keyof U]: U[key] extends Record<string, unknown>
50
+ ? U[key] extends infer V
51
+ ? { [key in keyof V]: V[key] }
52
+ : never
53
+ : U[key]
54
+ }
55
+ : never
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Methods for adding a new entry for a previous stage, rather
3
+ * than reusing the previous one.
4
+ * One two entries for the same stage have been added, `with`, `at` and
5
+ * `mock` methods can take an optional index to select the entry to target
6
+ */
7
+
8
+ import { ModularComponent } from '../modular-component'
9
+ import { AddStage, ModularStages, StageIndices, StageTuple } from '../stage'
10
+ import { ComputeArguments } from '../arguments'
11
+ import { RestrictValue } from '../validation'
12
+ import { MethodRecord } from '../methods'
13
+ import { FilterNever } from '../utils'
14
+
15
+ interface ModularAddMethod<
16
+ Props extends {},
17
+ Ref,
18
+ Methods extends Record<string, MethodRecord>,
19
+ Stages extends StageTuple,
20
+ Method extends keyof Methods,
21
+ Symbol extends keyof ModularStages = Methods[Method]['symbol'],
22
+ > {
23
+ <
24
+ Value extends RestrictValue<Arguments, Symbol>,
25
+ Arguments extends ComputeArguments<Props, Ref, Methods, Stages>,
26
+ >(
27
+ value: Value,
28
+ ): ModularComponent<
29
+ Props,
30
+ Ref,
31
+ Methods,
32
+ AddStage<Stages, { stage: Symbol; value: Value }>
33
+ >
34
+ }
35
+
36
+ export type ModularAddMethods<
37
+ Props extends {},
38
+ Ref,
39
+ Methods extends Record<string, MethodRecord>,
40
+ Stages extends StageTuple,
41
+ > = FilterNever<{
42
+ [Method in keyof Methods as `add${Method extends string
43
+ ? Method
44
+ : never}`]: StageIndices<
45
+ Stages,
46
+ Methods[Method]['symbol']
47
+ >['length'] extends 0
48
+ ? never
49
+ : ModularAddMethod<Props, Ref, Methods, Stages, Method>
50
+ }>
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Methods for rewinding a modular component up to a given stage.
3
+ * If multiple entries of the same stage were previously added,
4
+ * it's possible to pass the index for the entry to rewind to.
5
+ */
6
+
7
+ import { ModularComponent } from '../modular-component'
8
+ import { AtStage, StageIndices, StageTuple } from '../stage'
9
+ import { FilterNever, Last, ToIndices } from '../utils'
10
+ import { ValidateIndex } from '../validation'
11
+ import { MethodRecord } from '../methods'
12
+
13
+ interface ModularAtMethodIndices<
14
+ Props extends {},
15
+ Ref,
16
+ Methods extends Record<string, MethodRecord>,
17
+ Stages extends StageTuple,
18
+ Method extends keyof Methods,
19
+ Indices extends number[],
20
+ > {
21
+ <
22
+ // Validate index
23
+ StageIndex extends Index extends number ? Indices[Index] : Last<Indices>,
24
+ ValidIndex extends ValidateIndex<Index, ToIndices<Indices>>,
25
+ Index extends number,
26
+ >(
27
+ index: ValidIndex extends true ? Index : ToIndices<Indices>,
28
+ ): ModularComponent<Props, Ref, Methods, AtStage<Stages, StageIndex>>
29
+ }
30
+
31
+ interface ModularAtMethodLast<
32
+ Props extends {},
33
+ Ref,
34
+ Methods extends Record<string, MethodRecord>,
35
+ Stages extends StageTuple,
36
+ Method extends keyof Methods,
37
+ Index extends number,
38
+ > {
39
+ (): ModularComponent<Props, Ref, Methods, AtStage<Stages, Index>>
40
+ }
41
+
42
+ type ModularAtMethod<
43
+ Props extends {},
44
+ Ref,
45
+ Methods extends Record<string, MethodRecord>,
46
+ Stages extends StageTuple,
47
+ Method extends keyof Methods,
48
+ Symbol extends symbol = Methods[Method]['symbol'],
49
+ Indices extends StageIndices<Stages, Symbol> = StageIndices<Stages, Symbol>,
50
+ > = Indices['length'] extends 0
51
+ ? never
52
+ : ModularAtMethodIndices<Props, Ref, Methods, Stages, Method, Indices> &
53
+ ModularAtMethodLast<Props, Ref, Methods, Stages, Method, Last<Indices>>
54
+
55
+ export type ModularAtMethods<
56
+ Props extends {},
57
+ Ref,
58
+ Methods extends Record<string, MethodRecord>,
59
+ Stages extends StageTuple,
60
+ > = FilterNever<{
61
+ [Method in keyof Methods as `at${Method extends string
62
+ ? Method
63
+ : never}`]: ModularAtMethod<Props, Ref, Methods, Stages, Method>
64
+ }>
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Methods for converting a modular component to hooks, either
3
+ * a generic hook returning all the arguments, or a specific hook
4
+ * returning one given argument.
5
+ */
6
+
7
+ import { StageTuple } from '../stage'
8
+ import { MethodRecord } from '../methods'
9
+ import { ComputeArguments } from '../arguments'
10
+
11
+ export type ModularHookMethods<
12
+ Props extends {},
13
+ Ref,
14
+ Methods extends Record<string, MethodRecord>,
15
+ Stages extends StageTuple,
16
+ Arguments extends {} = ComputeArguments<Props, Ref, Methods, Stages>,
17
+ > = {
18
+ asHook(): keyof Props extends never
19
+ ? () => Arguments
20
+ : (props: Props) => Arguments
21
+ } & {
22
+ [Arg in keyof Arguments as `asUse${Capitalize<
23
+ Arg extends string ? Arg : never
24
+ >}`]: keyof Props extends never
25
+ ? () => () => Arguments[Arg]
26
+ : () => (props: Props) => Arguments[Arg]
27
+ }