@cleanweb/react 1.0.10 → 1.1.1-beta.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.
@@ -26,7 +26,8 @@ var MergedState = /** @class */ (function () {
26
26
  this.valueKeys = [];
27
27
  this._initialValues_ = __assign({}, initialState);
28
28
  this._values_ = __assign({}, initialState);
29
- Object.keys(initialState).forEach(function (key) {
29
+ Object.keys(initialState).forEach(function (_key) {
30
+ var key = _key;
30
31
  if (_this.reservedKeys.includes(key)) {
31
32
  throw new Error("The name \"".concat(key, "\" is reserved by CleanState and cannot be used to index state variables. Please use a different key."));
32
33
  }
@@ -3,7 +3,6 @@ export declare class ComponentMethods<TState extends object, TProps extends obje
3
3
  state: TCleanState<TState>;
4
4
  props: TProps;
5
5
  }
6
- type ComponentMethodsConstructor = typeof ComponentMethods<any, any>;
7
- type UseMethods = <MethodsClass extends ComponentMethodsConstructor>(Methods: MethodsClass, state: InstanceType<MethodsClass>['state'], props: InstanceType<MethodsClass>['props']) => InstanceType<MethodsClass>;
6
+ type UseMethods = <TMethods extends ComponentMethods<any, any>>(Methods: Constructor<TMethods>, state: TMethods['state'], props: TMethods['props']) => TMethods;
8
7
  export declare const useMethods: UseMethods;
9
8
  export {};
@@ -14,10 +14,10 @@ var useMethods = function (Methods, state, props) {
14
14
  // causing the instance to be unexpectedly recreated in the middle of the components lifecycle.
15
15
  // But useRef and useState values appear to always be preserved whenever this happens.
16
16
  // So those two are the only cross-render-persistence methods we can consider safe.
17
- var methods = (0, react_1.useMemo)(function () {
17
+ var methods = (0, react_1.useRef)((0, react_1.useMemo)(function () {
18
18
  // See useLogic implementation for a discussion of this type assertion.
19
19
  return new Methods();
20
- }, []);
20
+ }, [])).current;
21
21
  methods.state = state;
22
22
  methods.props = props;
23
23
  return methods;
@@ -1,4 +1,12 @@
1
- declare class CleanStateBase<TState extends object> {
1
+ /**
2
+ * Returns a value that is false before the component has been mounted,
3
+ * then true during all subsequent rerenders.
4
+ */
5
+ export declare const useMountState: () => boolean;
6
+ type PutState<TState extends object> = {
7
+ [Key in keyof TState]: React.Dispatch<React.SetStateAction<TState[Key]>>;
8
+ };
9
+ declare class CleanStateBase<TState extends Record<string, any>> {
2
10
  reservedKeys: string[];
3
11
  valueKeys: string[];
4
12
  private _values_;
@@ -6,20 +14,16 @@ declare class CleanStateBase<TState extends object> {
6
14
  private _setters_;
7
15
  constructor(initialState: TState);
8
16
  static update: <TState_1 extends object>(this: CleanStateBase<TState_1>) => void;
9
- get put(): { [Key in keyof TState]: (value: TState[Key]) => void; };
17
+ get put(): PutState<TState>;
10
18
  get initialState(): TState;
11
19
  putMany: (newValues: Partial<TState>) => void;
12
20
  }
13
21
  type TCleanStateInstance<TState extends object> = TState & CleanStateBase<TState>;
14
22
  export type TCleanState<TState extends object> = TCleanStateInstance<TState>;
15
- type StateInitFunction = (props?: object) => object;
16
- type TInitialState<StateParamType> = StateParamType extends StateInitFunction ? ReturnType<StateParamType> : StateParamType;
17
- type StateInitParameters<StateInitializer> = StateInitializer extends StateInitFunction ? Parameters<StateInitializer> : [];
18
- type UseCleanState = <StateInitializer extends StateInitFunction | object>(_initialState: StateInitializer, ...props: StateInitParameters<StateInitializer>) => TCleanState<TInitialState<StateInitializer>>;
19
- export declare const useCleanState: UseCleanState;
20
- /**
21
- * Returns a value that is false before the component has been mounted,
22
- * then true during all subsequent rerenders.
23
- */
24
- export declare const useMountState: () => boolean;
23
+ export type TState<YourCleanState extends CleanStateBase<{}>> = Omit<YourCleanState, keyof CleanStateBase<{}>>;
24
+ type StateInitFunction = (...args: any[]) => object;
25
+ type StateInit = object | StateInitFunction;
26
+ type TInitialState<Initializer extends StateInit> = Initializer extends (...args: any[]) => (infer TState extends object) ? TState : Initializer;
27
+ type TUseCleanState = <TInit extends StateInit>(_initialState: TInit, ...props: TInit extends (...args: infer TProps extends any[]) => (infer TState extends object) ? TProps : []) => TCleanStateInstance<TInitialState<TInit>>;
28
+ export declare const useCleanState: TUseCleanState;
25
29
  export {};
@@ -11,8 +11,26 @@ var __assign = (this && this.__assign) || function () {
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.useMountState = exports.useCleanState = void 0;
14
+ exports.useCleanState = exports.useMountState = void 0;
15
15
  var react_1 = require("react");
16
+ /**
17
+ * Returns a value that is false before the component has been mounted,
18
+ * then true during all subsequent rerenders.
19
+ */
20
+ var useMountState = function () {
21
+ /**
22
+ * This must not be a stateful value. It should not be the cause of a rerender.
23
+ * It merely provides information about the render count,
24
+ * without influencing that count itself.
25
+ * So `mounted` should never be set with `useState`.
26
+ */
27
+ var mounted = (0, react_1.useRef)(false);
28
+ (0, react_1.useEffect)(function () {
29
+ mounted.current = true;
30
+ }, []);
31
+ return mounted.current;
32
+ };
33
+ exports.useMountState = useMountState;
16
34
  var CleanStateBase = /** @class */ (function () {
17
35
  function CleanStateBase(initialState) {
18
36
  var _this = this;
@@ -81,7 +99,14 @@ var CleanStateBase = /** @class */ (function () {
81
99
  var retrieveState = react_1.useState;
82
100
  this.valueKeys.forEach(function (key) {
83
101
  var _a;
84
- _a = retrieveState(_this.initialState[key]), _this._values_[key] = _a[0], _this._setters_[key] = _a[1];
102
+ // @todo Make state updates accessible immediately. Use state.staged to access the scheduled updates.
103
+ var setter;
104
+ // @todo Support SetStateAction callback signature in state.put(...);
105
+ _a = retrieveState(_this.initialState[key]), _this._values_[key] = _a[0], setter = _a[1];
106
+ _this._setters_[key] = (function (valueOrCallback) {
107
+ // this._staged_[key] = value;
108
+ setter(valueOrCallback);
109
+ });
85
110
  });
86
111
  /* Object.entries<TUseStateArray<TState>>(stateAndSetters).forEach(([key, responseFromUseState]) => {
87
112
  [this._values_[key], this._setters_[key]] = responseFromUseState;
@@ -92,29 +117,37 @@ var CleanStateBase = /** @class */ (function () {
92
117
  }());
93
118
  ;
94
119
  var CleanState = CleanStateBase;
95
- var useCleanState = function (_initialState, props) {
96
- if (props === void 0) { props = {}; }
97
- var initialState = typeof _initialState === 'function' ? (0, react_1.useMemo)(function () { return _initialState(props); }, []) : _initialState;
98
- var cleanState = (0, react_1.useMemo)(function () { return new CleanState(initialState); }, []);
120
+ var useCleanState = function (_initialState) {
121
+ var props = [];
122
+ for (var _i = 1; _i < arguments.length; _i++) {
123
+ props[_i - 1] = arguments[_i];
124
+ }
125
+ var mounted = (0, exports.useMountState)();
126
+ var initialState = typeof _initialState === 'function'
127
+ ? (0, react_1.useMemo)(function () { return _initialState.apply(void 0, props); }, [])
128
+ : _initialState;
129
+ ;
130
+ var freshInstance = {};
131
+ if (!mounted)
132
+ freshInstance = new CleanState(initialState);
133
+ if (!freshInstance.put)
134
+ throw new Error('useCleanState failed to initialized a state instance.');
135
+ var cleanState = (0, react_1.useRef)(freshInstance).current;
99
136
  CleanState.update.call(cleanState);
100
137
  return cleanState;
101
138
  };
102
139
  exports.useCleanState = useCleanState;
103
- /**
104
- * Returns a value that is false before the component has been mounted,
105
- * then true during all subsequent rerenders.
106
- */
107
- var useMountState = function () {
108
- /**
109
- * This must not be a stateful value. It should not be the cause of a rerender.
110
- * It merely provides information about the render count,
111
- * without influencing that count itself.
112
- * So `mounted` should never be set with `useState`.
113
- */
114
- var mounted = (0, react_1.useRef)(false);
115
- (0, react_1.useEffect)(function () {
116
- mounted.current = true;
117
- }, []);
118
- return mounted.current;
119
- };
120
- exports.useMountState = useMountState;
140
+ // Should be valid.
141
+ // useCleanState((a: number) => ({b: a.toString(), q: 1}), 6);
142
+ // useCleanState((a: boolean) => ({b: a.toString()}), true);
143
+ // useCleanState((a: number, c?: string) => ({ b: `${a}` }), 6);
144
+ // useCleanState((a: number, c?: string) => ({ b: `${a}` }), 6, 'word');
145
+ // useCleanState((a: number, c: string) => ({ b: a + c, f: true }), 6, 'text');
146
+ // useCleanState({ d: 5000 });
147
+ // Should fail.
148
+ // useCleanState((a: number) => ({b: a.toString(), q: 1}), 6, false);
149
+ // useCleanState((a: boolean) => ({b: a.toString()}));
150
+ // useCleanState((a: number, c?: string) => ({ b: `${a}` }), '6');
151
+ // useCleanState((a: number, c?: string) => ({ b: `${a}` }));
152
+ // useCleanState((a: number, c: string) => ({ b: a + c, f: true }), 6, 7);
153
+ // useCleanState({ d: 5000 }, true);
@@ -1,13 +1,12 @@
1
- import type { ReactElement } from 'react';
2
- import type { ComponentInstanceConstructor } from './instance';
1
+ import type { VoidFunctionComponent } from 'react';
2
+ import type { TComponentClass } from './logic';
3
3
  import { ComponentInstance } from './instance';
4
- type Obj = Record<string, any>;
5
- type IComponentConstructor = ComponentInstanceConstructor<any, any, any> & typeof ClassComponent<any, any, any>;
6
- export declare class ClassComponent<TState extends Obj, TProps extends Obj, THooks extends Obj> extends ComponentInstance<TState, TProps, THooks> {
7
- Render: () => ReactElement<any, any> | null;
8
- static FC: <IComponentType extends IComponentConstructor>(this: IComponentType, _Component?: IComponentType) => (props: InstanceType<IComponentType>["props"]) => ReactElement<any, any> | null;
4
+ type Extractor = <TComponent extends ClassComponent<object, object, object>>(this: TComponentClass<TComponent, typeof ClassComponent>, _Component?: TComponentClass<TComponent, typeof ClassComponent>) => VoidFunctionComponent;
5
+ export declare class ClassComponent<TState extends object = EmptyObject, TProps extends object = EmptyObject, THooks extends object = EmptyObject> extends ComponentInstance<TState, TProps, THooks> {
6
+ Render: VoidFunctionComponent<{}>;
7
+ static renderAs: 'component' | 'template';
8
+ static FC: Extractor;
9
9
  }
10
- type AnyFunction = (...args: any) => any;
11
10
  interface HookWrapperProps<THookFunction extends AnyFunction> {
12
11
  hook: THookFunction;
13
12
  argumentsList: Parameters<THookFunction>;
@@ -16,6 +16,7 @@ var __extends = (this && this.__extends) || (function () {
16
16
  })();
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.Use = exports.ClassComponent = void 0;
19
+ var jsx_runtime_1 = require("react/jsx-runtime");
19
20
  var react_1 = require("react");
20
21
  var instance_1 = require("./instance");
21
22
  /** Provide more useful stack traces for otherwise non-specific function names. */
@@ -31,37 +32,56 @@ var setFunctionName = function (func, newName) {
31
32
  console.warn(error);
32
33
  }
33
34
  };
35
+ // eslint-enable no-use-before-define
34
36
  var ClassComponent = /** @class */ (function (_super) {
35
37
  __extends(ClassComponent, _super);
36
38
  function ClassComponent() {
37
39
  return _super !== null && _super.apply(this, arguments) || this;
38
40
  }
41
+ ClassComponent.renderAs = 'component';
39
42
  ClassComponent.FC = function FC(_Component) {
40
- var Component = _Component || this;
43
+ var Component = _Component !== null && _Component !== void 0 ? _Component : this;
41
44
  var isClassComponentType = Component.prototype instanceof ClassComponent;
42
45
  if (!Component.getInitialState || !isClassComponentType)
43
46
  throw new Error('Attempted to initialize ClassComponent with invalid Class type. Either pass a class that extends ClassComponent to FC (e.g `export FC(MyComponent);`), or ensure it is called as a method on a ClassComponent constructor type (e.g `export MyComponent.FC()`).');
44
- var Wrapper = function (props) {
47
+ var Wrapper = function (props, context) {
45
48
  var Render = (0, instance_1.useInstance)(Component, props).Render;
46
49
  // Add calling component name to Render function name in stack traces.
47
- (0, react_1.useMemo)(function () { return setFunctionName(Render, "".concat(Component.name, " > Render")); }, [Render]);
50
+ (0, react_1.useMemo)(function () { return setFunctionName(Render, "".concat(Component.name, ".Render")); }, [Render]);
48
51
  /**
49
- * It may be impossible to set state within the body of Render,
52
+ * Normally a component can update it's own state in the "before-render" stage to
53
+ * skip DOM updates and trigger and immediate rerun of the rendering with the new state.
54
+ *
55
+ * It may be impossible to do this within the body of Render, if we call it as JSX here,
50
56
  * since technically, the Wrapper component owns the state and not the Render component.
51
- * Consider using this as a function call instead of JSX to avoid that.
52
- */
53
- // if (instance.renderAs === 'component') return <Render />;
54
- return Render();
57
+ * Using it as JSX establishes a component boundary, and React will throw an error if we try to set
58
+ * state in the "before-render" stage of `Render`, since it will be attempting to update it's parent's
59
+ * state (i.e `Wrapper` component) rather than it's own state.
60
+ *
61
+ * Consider using this as a function call instead of JSX to avoid that. This way, we avoid
62
+ * establishing a component boundary between `Wrapper` and `Render`.
63
+ *
64
+ * Although, since beforeRender() is called earlier from a hook, this is probably
65
+ * a non-issue. It will only force users to move their logic into `beforeRender` instead
66
+ * of doing it directly in `Render`. This might mean cleaner Render functions,
67
+ * so there's probably no real value lost if we keep the component boundary.
68
+ **/
69
+ if (Component.renderAs === 'template')
70
+ return Render({}, context);
71
+ // With the existence of useContext(),
72
+ // what exactly does the context argument to FunctionComponent represent?
73
+ // Do we need to find a way to pass that context value to <Render /> here?
74
+ return (0, jsx_runtime_1.jsx)(Render, {});
55
75
  };
56
76
  // Include calling component name in wrapper function name on stack traces.
57
- setFunctionName(Wrapper, "".concat(Component.name, " > ").concat(Wrapper.name));
77
+ setFunctionName(Wrapper, "".concat(Component.name, " < Wrapper")); // ${Wrapper.name}
58
78
  return Wrapper;
59
79
  };
60
80
  return ClassComponent;
61
81
  }(instance_1.ComponentInstance));
62
82
  exports.ClassComponent = ClassComponent;
63
- var Use = function (_a) {
64
- var useGenericHook = _a.hook, argumentsList = _a.argumentsList, onUpdate = _a.onUpdate;
83
+ var Use = function (params) {
84
+ var useGenericHook = params.hook, argumentsList = params.argumentsList, onUpdate = params.onUpdate;
65
85
  var output = useGenericHook.apply(void 0, argumentsList);
66
86
  (0, react_1.useEffect)(function () {
67
87
  onUpdate(output);
@@ -1,9 +1,8 @@
1
- import type { ComponentLogicConstructor } from './logic';
1
+ import type { TComponentClass } from './logic';
2
2
  import { ComponentLogic } from './logic';
3
- type Obj = Record<string, any>;
4
3
  type AsyncAllowedEffectCallback = () => Awaitable<IVoidFunction>;
5
4
  export declare const noOp: () => void;
6
- export declare class ComponentInstance<TState extends Obj = {}, TProps extends Obj = {}, THooks extends Obj = {}> extends ComponentLogic<TState, TProps, THooks> {
5
+ export declare class ComponentInstance<TState extends object = EmptyObject, TProps extends object = EmptyObject, THooks extends object = EmptyObject> extends ComponentLogic<TState, TProps, THooks> {
7
6
  /**
8
7
  * Runs only _before_ first render, i.e before the component instance is mounted.
9
8
  * Useful for logic that is involved in determining what to render.
@@ -48,10 +47,17 @@ export declare class ComponentInstance<TState extends Obj = {}, TProps extends O
48
47
  */
49
48
  cleanUp: IVoidFunction;
50
49
  }
51
- type ComponentClassBaseType<TState extends Obj = {}, TProps extends Obj = {}, THooks extends Obj = {}> = ComponentLogicConstructor<TState, TProps, THooks> & Constructor<ComponentInstance<TState, TProps, THooks>>;
52
- export interface ComponentInstanceConstructor<TState extends Obj = {}, TProps extends Obj = {}, THooks extends Obj = {}> extends ComponentClassBaseType<TState, TProps, THooks> {
53
- }
54
- type UseInstance = <TClass extends ComponentInstanceConstructor>(Class: TClass, props: InstanceType<TClass>['props']) => InstanceType<TClass>;
55
- export declare const useMountCallbacks: <TInstance extends ComponentInstance<any, any, any>>(instance: TInstance) => void;
50
+ type UseInstance = <TClass extends ComponentInstance<object, object, object>>(Class: TComponentClass<TClass>, ...props: valueof<TClass['props']> extends never ? ([] | [EmptyObject] | [TClass['props']]) : [TClass['props']]) => TClass;
51
+ /**
52
+ * To ensure successful type checking, the second parameter must be written with spread syntax.
53
+ * Likely because of the `exactOptionalPropertyTypes` config option turned on,
54
+ * and `UseInstance` using an empty tuple in its rest parameter type, attempting to simply
55
+ * retrieve the second argument directly causes an error when that argument is passed on to `useLogic`.
56
+ * But directly working with the rest array bypasses the problem. Also note that the issue persists even when
57
+ * the second param is given `{}` as a default follow to account for the empty tuple case. TypeScript
58
+ * just wants us to use the rest parameter explicitly by force.
59
+ */
56
60
  export declare const useInstance: UseInstance;
61
+ type UseMountCallbacks = <TInstance extends ComponentInstance<any, any, any>>(instance: TInstance) => void;
62
+ export declare const useMountCallbacks: UseMountCallbacks;
57
63
  export {};
@@ -14,8 +14,17 @@ var __extends = (this && this.__extends) || (function () {
14
14
  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
15
  };
16
16
  })();
17
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
18
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
19
+ if (ar || !(i in from)) {
20
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
21
+ ar[i] = from[i];
22
+ }
23
+ }
24
+ return to.concat(ar || Array.prototype.slice.call(from));
25
+ };
17
26
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.useInstance = exports.useMountCallbacks = exports.ComponentInstance = exports.noOp = void 0;
27
+ exports.useMountCallbacks = exports.useInstance = exports.ComponentInstance = exports.noOp = void 0;
19
28
  var react_1 = require("react");
20
29
  var state_1 = require("../base/state");
21
30
  var logic_1 = require("./logic");
@@ -74,6 +83,56 @@ var ComponentInstance = /** @class */ (function (_super) {
74
83
  }(logic_1.ComponentLogic));
75
84
  exports.ComponentInstance = ComponentInstance;
76
85
  ;
86
+ /**
87
+ * To ensure successful type checking, the second parameter must be written with spread syntax.
88
+ * Likely because of the `exactOptionalPropertyTypes` config option turned on,
89
+ * and `UseInstance` using an empty tuple in its rest parameter type, attempting to simply
90
+ * retrieve the second argument directly causes an error when that argument is passed on to `useLogic`.
91
+ * But directly working with the rest array bypasses the problem. Also note that the issue persists even when
92
+ * the second param is given `{}` as a default follow to account for the empty tuple case. TypeScript
93
+ * just wants us to use the rest parameter explicitly by force.
94
+ */
95
+ var useInstance = function (Component) {
96
+ var _a;
97
+ var args = [];
98
+ for (var _i = 1; _i < arguments.length; _i++) {
99
+ args[_i - 1] = arguments[_i];
100
+ }
101
+ // useHooks.
102
+ var instance = logic_1.useLogic.apply(void 0, __spreadArray([Component], args, false)); // Must spread rest parameter, rather than passing a single `props` argument directly.
103
+ /**
104
+ * Argument of type '
105
+ * [
106
+ (valueof<TClass["props"]> extends never
107
+ ? [] | [CEmptyObject]
108
+ : [ TClass["props"] ]
109
+ )[0]
110
+ ]
111
+ ' is not assignable to parameter of type '
112
+ valueof<TClass["props"]> extends never
113
+ ? [] | [CEmptyObject] // | [undefined]
114
+ : [ TClass["props"] ]
115
+ '
116
+ */
117
+ // beforeMount, onMount, cleanUp.
118
+ // eslint-disable-next-line no-use-before-define
119
+ (0, exports.useMountCallbacks)(instance);
120
+ // beforeRender.
121
+ (_a = instance.beforeRender) === null || _a === void 0 ? void 0 : _a.call(instance);
122
+ // onRender.
123
+ (0, react_1.useEffect)(function () {
124
+ var _a;
125
+ var cleanupAfterRerender = (_a = instance.onRender) === null || _a === void 0 ? void 0 : _a.call(instance);
126
+ return function () {
127
+ if (typeof cleanupAfterRerender === 'function')
128
+ cleanupAfterRerender();
129
+ else
130
+ cleanupAfterRerender === null || cleanupAfterRerender === void 0 ? void 0 : cleanupAfterRerender.then(function (cleanUp) { return cleanUp === null || cleanUp === void 0 ? void 0 : cleanUp(); });
131
+ };
132
+ });
133
+ return instance;
134
+ };
135
+ exports.useInstance = useInstance;
77
136
  var useMountCallbacks = function (instance) {
78
137
  var _a;
79
138
  var mounted = (0, state_1.useMountState)();
@@ -99,25 +158,3 @@ var useMountCallbacks = function (instance) {
99
158
  }, []);
100
159
  };
101
160
  exports.useMountCallbacks = useMountCallbacks;
102
- var useInstance = function (Component, props) {
103
- var _a;
104
- // useHooks.
105
- var instance = (0, logic_1.useLogic)(Component, props);
106
- // beforeMount, onMount, cleanUp.
107
- (0, exports.useMountCallbacks)(instance);
108
- // beforeRender.
109
- (_a = instance.beforeRender) === null || _a === void 0 ? void 0 : _a.call(instance);
110
- // onRender.
111
- (0, react_1.useEffect)(function () {
112
- var _a;
113
- var cleanupAfterRerender = (_a = instance.onRender) === null || _a === void 0 ? void 0 : _a.call(instance);
114
- return function () {
115
- if (typeof cleanupAfterRerender === 'function')
116
- cleanupAfterRerender();
117
- else
118
- cleanupAfterRerender === null || cleanupAfterRerender === void 0 ? void 0 : cleanupAfterRerender.then(function (cleanUp) { return cleanUp === null || cleanUp === void 0 ? void 0 : cleanUp(); });
119
- };
120
- });
121
- return instance;
122
- };
123
- exports.useInstance = useInstance;
@@ -1,13 +1,22 @@
1
- import type { TCleanState } from '../base/state';
2
- export declare class ComponentLogic<TState extends object, TProps extends object, THooks extends object> {
1
+ import type { TCleanState, TState } from '../base/state';
2
+ export declare class ComponentLogic<TState extends object = EmptyObject, TProps extends object = EmptyObject, THooks extends object = EmptyObject> {
3
3
  state: TCleanState<TState>;
4
4
  props: TProps;
5
5
  hooks: THooks;
6
+ static getInitialState: IComponentClass['getInitialState'];
6
7
  useHooks?: () => THooks;
7
8
  }
8
- export interface ComponentLogicConstructor<TState extends object, TProps extends object, THooks extends object> extends Constructor<ComponentLogic<TState, TProps, THooks>> {
9
- getInitialState: (props?: TProps) => TState;
9
+ type CnstPrm = ConstructorParameters<typeof ComponentLogic>;
10
+ export interface IComponentClass<Instance extends ComponentLogic = ComponentLogic> {
11
+ new (...params: CnstPrm): Instance;
12
+ getInitialState: (props?: Instance['props']) => TState<Instance['state']>;
10
13
  }
11
- type UseLogic = <LogicClass extends ComponentLogicConstructor<{}, object, any>>(Methods: LogicClass, props?: InstanceType<LogicClass>['props']) => InstanceType<LogicClass>;
14
+ export type ComponentClassStatics<Instance extends ComponentLogic<object, object, object>> = {
15
+ getInitialState: (props?: Instance['props']) => TState<Instance['state']>;
16
+ };
17
+ export type TComponentClass<Instance extends ComponentLogic<object, object, object>, Statics extends ComponentClassStatics<Instance> = ComponentClassStatics<Instance>, Params extends CnstPrm = CnstPrm> = Statics & Constructor<Instance, Params>;
18
+ export interface IEmpty extends EmptyObject {
19
+ }
20
+ type UseLogic = <CLogic extends ComponentLogic<object, object, object>>(Methods: TComponentClass<CLogic>, ...props: valueof<CLogic['props']> extends never ? ([] | [EmptyObject] | [CLogic['props']]) : [CLogic['props']]) => CLogic;
12
21
  export declare const useLogic: UseLogic;
13
22
  export {};
@@ -1,4 +1,20 @@
1
1
  "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var _a;
2
18
  Object.defineProperty(exports, "__esModule", { value: true });
3
19
  exports.useLogic = exports.ComponentLogic = void 0;
4
20
  var react_1 = require("react");
@@ -10,22 +26,42 @@ var ComponentLogic = /** @class */ (function () {
10
26
  }());
11
27
  exports.ComponentLogic = ComponentLogic;
12
28
  ;
29
+ testing: {
30
+ var A = (_a = /** @class */ (function (_super) {
31
+ __extends(C, _super);
32
+ function C() {
33
+ return _super !== null && _super.apply(this, arguments) || this;
34
+ }
35
+ return C;
36
+ }(ComponentLogic)),
37
+ _a.getInitialState = function () { return ({}); },
38
+ _a);
39
+ A.getInitialState();
40
+ }
41
+ ;
13
42
  var useLogic = function (Methods, props) {
14
- var _a;
43
+ var _b;
15
44
  if (props === void 0) { props = {}; }
16
45
  var state = (0, state_1.useCleanState)(Methods.getInitialState, props);
17
- // There's apparently a bug? with Typescript that pegs the return type of "new Methods()" to "ComponentLogic<{}, {}, {}>",
18
- // completely ignoring the type specified for Methods in the function's type definition.
19
- // `new Methods()` should return whatever the InstanceType of TClass is, as that is the type explicitly specified for Methods.
20
- // Ignoring the specified type to gin up something else and then complain about it is quite weird.
21
- // Regardless, even when `extends ComponentLogicConstructor<TState, TProps, THooks>` is specified using generics instead of a set type,
22
- // the issue persists. Which is absurd since this should ensure that InstanceType<Class> should exactly match ComponentLogic<TState, TProps, THooks>
23
- var methods = (0, react_1.useMemo)(function () {
46
+ var methods = (0, react_1.useRef)((0, react_1.useMemo)(function () {
24
47
  return new Methods();
25
- }, []);
48
+ }, [])).current;
26
49
  methods.state = state;
27
50
  methods.props = props;
28
- methods.hooks = ((_a = methods.useHooks) === null || _a === void 0 ? void 0 : _a.call(methods)) || {};
51
+ methods.hooks = ((_b = methods.useHooks) === null || _b === void 0 ? void 0 : _b.call(methods)) || {};
29
52
  return methods;
30
53
  };
31
54
  exports.useLogic = useLogic;
55
+ testing: {
56
+ var a = { b: '' };
57
+ var MyComponentLogic = /** @class */ (function (_super) {
58
+ __extends(MyComponentLogic, _super);
59
+ function MyComponentLogic() {
60
+ return _super !== null && _super.apply(this, arguments) || this;
61
+ }
62
+ MyComponentLogic.getInitialState = function () { return ({}); };
63
+ return MyComponentLogic;
64
+ }(ComponentLogic));
65
+ ;
66
+ (0, exports.useLogic)(MyComponentLogic);
67
+ }
@@ -1,56 +1,67 @@
1
-
2
- type Optional<
3
- BaseType,
4
- AllowNull extends boolean = true
5
- > = (
6
- AllowNull extends true
7
- ? BaseType | undefined | null
8
- : BaseType | undefined
9
- )
10
-
11
- type Awaitable<Type> = Type | Promise<Type>;
12
-
13
- type Constructor<
14
- TInstance extends any = any,
15
- TParams extends any[] = never[]
16
- > = new (...args: TParams) => TInstance
17
-
18
-
19
1
  /**
2
+ * @file
3
+ * This file is an "Ambient declarations file". The types defined here are available globally.
4
+ * More info here: https://stackoverflow.com/a/73389225/985454
5
+ *
6
+ * Don't use `import` and `export` in this file directly! It breaks ambience.
7
+ * To import external types in an ambient declarations file (this file) use the following:
8
+ *
20
9
  * @example
21
- * ```js
22
- * const getNumber: AsyncFunction<number> = async () => {
23
- * return 5;
10
+ * declare type React = typeof import('react');
11
+ *
12
+ * To contribute ambient declarations from any file, even non-ambient ones, use this:
13
+ *
14
+ * @example
15
+ * declare global {
16
+ * interface Window {
17
+ * ethereum: any
18
+ * }
24
19
  * }
25
- * ```
26
- */
27
- declare type AsyncFunction<
28
- TReturnValue extends any = void,
29
- Params extends any[] = never[]
30
- > = (...params: Params) => Promise<TReturnValue>
31
-
32
- /**
33
- * A function that takes no arguments and returns nothing.
34
- * Pass a type argument to set whether `async` and/or `sync` functions are allowed.
35
- */
36
- declare interface IVoidFunction<AsyncType extends 'async' | 'sync' | 'both' = 'both'> {
37
- (): AsyncType extends 'async' ? Promise<void>
38
- : AsyncType extends 'sync' ? void
39
- : Promise<void> | void
40
- }
41
-
42
- declare type FunctionType = (...args: any[]) => any;
43
-
44
-
45
- declare interface Window {
46
- }
47
-
48
- declare namespace JSX {
49
- interface IntrinsicElements {
50
- }
51
- }
52
-
53
- declare namespace NodeJS {
54
- interface ProcessEnv {
55
- }
20
+ **/
21
+ /** */
22
+ declare global {
23
+ type Optional<BaseType, AllowNull extends boolean = true> = (AllowNull extends true ? BaseType | undefined | null : BaseType | undefined);
24
+ type Awaitable<Type> = Type | Promise<Type>;
25
+ type Constructor<TInstance extends any = any, TParams extends any[] = never[]> = new (...args: TParams) => TInstance;
26
+ /**
27
+ * @example
28
+ * ```js
29
+ * const getNumber: AsyncFunction<number> = async () => {
30
+ * return 5;
31
+ * }
32
+ * ```
33
+ */
34
+ type AsyncFunction<TReturnValue extends any = void, Params extends any[] = never[]> = (...params: Params) => Promise<TReturnValue>;
35
+ /**
36
+ * A function that takes no arguments and returns nothing.
37
+ * Pass a type argument to set whether `async` and/or `sync` functions are allowed.
38
+ */
39
+ interface IVoidFunction<AsyncType extends 'async' | 'sync' | 'both' = 'both'> {
40
+ (): AsyncType extends 'async' ? Promise<void> : AsyncType extends 'sync' ? void : Promise<void> | void;
41
+ }
42
+ type AnyFunction = (...args: any) => any;
43
+ type FunctionType = AnyFunction;
44
+ type TFunction = AnyFunction;
45
+ interface Window {
46
+ }
47
+ namespace JSX {
48
+ interface IntrinsicElements {
49
+ }
50
+ }
51
+ namespace NodeJS {
52
+ interface ProcessEnv {
53
+ }
54
+ }
55
+ type __FromPrivateHelpers = typeof import('./globals.private');
56
+ type TEmptyObject1 = {
57
+ ''?: never;
58
+ };
59
+ type TEmptyObject2 = Record<symbol, never>;
60
+ type EmptyObject = __FromPrivateHelpers['EmptyObject'];
61
+ type EmptyObject2 = __FromPrivateHelpers['EmptyObject2'];
62
+ type EmptyObject3 = __FromPrivateHelpers['EmptyObject3'];
63
+ type valueof<TObject> = TObject[keyof TObject];
64
+ interface T extends __FromPrivateHelpers {
65
+ }
56
66
  }
67
+ export {};
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /**
3
+ * @file
4
+ * This file is an "Ambient declarations file". The types defined here are available globally.
5
+ * More info here: https://stackoverflow.com/a/73389225/985454
6
+ *
7
+ * Don't use `import` and `export` in this file directly! It breaks ambience.
8
+ * To import external types in an ambient declarations file (this file) use the following:
9
+ *
10
+ * @example
11
+ * declare type React = typeof import('react');
12
+ *
13
+ * To contribute ambient declarations from any file, even non-ambient ones, use this:
14
+ *
15
+ * @example
16
+ * declare global {
17
+ * interface Window {
18
+ * ethereum: any
19
+ * }
20
+ * }
21
+ **/
22
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @file
3
+ * This file is created specifically to augment the declarations in
4
+ * [globals.d.ts]({@link ./globals.d.ts}).
5
+ *
6
+ * **You should not import this file directly.**
7
+ */
8
+ /** */
9
+ declare const UniqueSecretSymbolKey: unique symbol;
10
+ declare class CEmptyObject {
11
+ [key: keyof any]: never;
12
+ }
13
+ declare class CEmptyObject2 {
14
+ [UniqueSecretSymbolKey]?: never;
15
+ }
16
+ declare class CEmptyObject3 {
17
+ /**
18
+ * It appears keys of the base `symbol` type are excluded from
19
+ * excess property checks. This is likely a bug in TypeScript.
20
+ * Even the "has no properties in common" error disappears if the
21
+ * value being placed into a variable has a key typed as `symbol`.
22
+ * This only applies to the base `symbol` type. Specifc `'unique symbol'`
23
+ * types are unaffected.
24
+ *
25
+ * @example
26
+ * // Consider the following object:
27
+ * const myUniqueSymbol = Symbol('lkjhgfc');
28
+ * let myObj = { [myUniqueSymbol]?: 'a string value' };
29
+ *
30
+ * // We can attempt to reassign `myObj` with the expectation that TS will
31
+ * // warn if any key other than `myUniqueSymbol` is used in the new object.
32
+ * // But this breaks in one specific scenario.
33
+ *
34
+ * // No excess property check when this is used as a key.
35
+ * // Error "no properties in common" also suppressed when this is used as a key.
36
+ * const differentBasicSymbol = Symbol('qwertiop[') as symbol;
37
+ * myObj = { [differentBasicSymbol]: 5 };
38
+ *
39
+ * // Errors emitted as expected when this is used as a key.
40
+ * const differentUniqueSymbol = Symbol('zxcvbnm');
41
+ * myObj = { [differentUniqueSymbol]: 5 };
42
+ */
43
+ [key: symbol]: never;
44
+ }
45
+ export declare const EmptyObject: CEmptyObject;
46
+ export declare const EmptyObject2: CEmptyObject2;
47
+ export declare const EmptyObject3: CEmptyObject3;
48
+ export {};
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * @file
4
+ * This file is created specifically to augment the declarations in
5
+ * [globals.d.ts]({@link ./globals.d.ts}).
6
+ *
7
+ * **You should not import this file directly.**
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.EmptyObject3 = exports.EmptyObject2 = exports.EmptyObject = void 0;
11
+ /** */
12
+ var UniqueSecretSymbolKey = Symbol('asdfghjkliuytrewqaxcvb,nb');
13
+ var CEmptyObject = /** @class */ (function () {
14
+ function CEmptyObject() {
15
+ }
16
+ return CEmptyObject;
17
+ }());
18
+ var CEmptyObject2 = /** @class */ (function () {
19
+ function CEmptyObject2() {
20
+ }
21
+ return CEmptyObject2;
22
+ }());
23
+ var CEmptyObject3 = /** @class */ (function () {
24
+ function CEmptyObject3() {
25
+ }
26
+ return CEmptyObject3;
27
+ }());
28
+ exports.EmptyObject = new CEmptyObject();
29
+ exports.EmptyObject2 = new CEmptyObject2();
30
+ exports.EmptyObject3 = new CEmptyObject3();
31
+ testing: {
32
+ var mySymbol = Symbol('asdfgh');
33
+ var tt = {
34
+ // [mySymbol]: '' as never,
35
+ // [UniqueSecretSymbolKey]: '',
36
+ // '': '',
37
+ };
38
+ var TT = new CEmptyObject();
39
+ TT = tt;
40
+ }
@@ -28,10 +28,13 @@
28
28
  "resolveJsonModule": true,
29
29
  "isolatedModules": true,
30
30
  "jsx": "react-jsx",
31
- "strictNullChecks": true
31
+ "strictNullChecks": true,
32
+ "noImplicitAny": true,
33
+ "noUncheckedIndexedAccess": true,
34
+ "strictBindCallApply": true,
35
+ // "exactOptionalPropertyTypes": true
32
36
  },
33
37
  "include": [
34
- "next-env.d.ts",
35
38
  "**/*.ts",
36
39
  "**/*.tsx"
37
40
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleanweb/react",
3
- "version": "1.0.10",
3
+ "version": "1.1.1-beta.0",
4
4
  "description": "A suite of helpers for writing cleaner React function components.",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -34,8 +34,17 @@
34
34
  "_": "",
35
35
  "prepublishOnly": "npm run build",
36
36
  "publish:patch": "npm version patch && npm publish",
37
+ "publish:minor": "npm version minor && npm publish",
38
+ "publish:major": "npm version major && npm publish",
39
+ "__": "/// Increment beta number for the current patch version. ///",
40
+ "publish:beta:current": "npm version prerelease --preid beta && npm publish --tag beta",
41
+ "___": "/// Create a beta.0 for a new patch/minor/major version ///",
42
+ "publish:beta:new-patch": "npm version prepatch --preid beta && npm publish --tag beta",
43
+ "publish:beta:new-minor": "npm version preminor --preid beta && npm publish --tag beta",
44
+ "publish:beta:new-major": "npm version premajor --preid beta && npm publish --tag beta",
45
+ "____": "",
37
46
  "//postpublish": "cd ./mirror-pkg && npm publish && cd ..",
38
- "__": "",
47
+ "______": "",
39
48
  "test": "echo \"No tests ATM\""
40
49
  },
41
50
  "keywords": [
@@ -55,9 +64,16 @@
55
64
  },
56
65
  "license": "MIT",
57
66
  "devDependencies": {
67
+ "@babel/eslint-parser": "^7.25.9",
68
+ "@babel/preset-typescript": "^7.26.0",
58
69
  "@types/node": "20.14.10",
59
70
  "@types/react": "^16",
71
+ "babel-preset-react-app": "^10.0.1",
60
72
  "copyfiles": "^2.4.1",
73
+ "eslint": "^9.15.0",
74
+ "eslint-plugin-jsdoc": "^50.5.0",
75
+ "eslint-plugin-react": "^7.37.2",
76
+ "globals": "^15.12.0",
61
77
  "rimraf": "^6.0.1",
62
78
  "tsc-alias": "1.8.10",
63
79
  "typescript": "^5.6.2"