Skip to content

Commit 81e1497

Browse files
committedFeb 25, 2025
refactor: logic
1 parent a52f95f commit 81e1497

17 files changed

+134
-118
lines changed
 

‎eslint.config.mjs

+25-27
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,43 @@
11
/** @type {import('eslint').Linter.Config[]} */
2-
import js from "@eslint/js";
3-
import eslintConfigPrettier from "eslint-config-prettier";
4-
import prettierPlugin from "eslint-plugin-prettier";
5-
import reactHooks from "eslint-plugin-react-hooks";
6-
import reactRefresh from "eslint-plugin-react-refresh";
7-
import globals from "globals";
8-
import tseslint from "typescript-eslint";
2+
import js from '@eslint/js';
3+
import eslintConfigPrettier from 'eslint-config-prettier';
4+
import prettierPlugin from 'eslint-plugin-prettier';
5+
import reactHooks from 'eslint-plugin-react-hooks';
6+
import reactRefresh from 'eslint-plugin-react-refresh';
7+
import globals from 'globals';
8+
import tseslint from 'typescript-eslint';
99

1010
export default tseslint.config(
11-
{ ignores: ["dist", "node_modules"] },
11+
{ ignores: ['dist', 'node_modules'] },
1212
{
1313
extends: [js.configs.recommended, ...tseslint.configs.recommended],
14-
files: ["**/*.{ts,tsx}"],
14+
files: ['**/*.{ts,tsx}'],
1515
languageOptions: {
16-
ecmaVersion: "latest",
16+
ecmaVersion: 'latest',
1717
globals: globals.browser,
1818
},
1919
plugins: {
20-
"react-hooks": reactHooks,
21-
"react-refresh": reactRefresh,
20+
'react-hooks': reactHooks,
21+
'react-refresh': reactRefresh,
2222
prettier: prettierPlugin,
2323
},
2424
rules: {
2525
...reactHooks.configs.recommended.rules,
26-
"react-refresh/only-export-components": [
27-
"warn",
28-
{ allowConstantExport: true },
29-
],
26+
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
3027
...eslintConfigPrettier.rules,
31-
eqeqeq: ["error", "always"],
32-
"@typescript-eslint/no-explicit-any": "off",
33-
"@typescript-eslint/triple-slash-reference": "off",
34-
"@typescript-eslint/no-unused-vars": [
35-
"error",
28+
eqeqeq: ['error', 'always'],
29+
'@typescript-eslint/no-explicit-any': 'off',
30+
'@typescript-eslint/triple-slash-reference': 'off',
31+
'@typescript-eslint/no-empty-object-type': 'off',
32+
'@typescript-eslint/no-unused-vars': [
33+
'error',
3634
{
37-
args: "all",
38-
argsIgnorePattern: "^_",
39-
caughtErrors: "all",
40-
caughtErrorsIgnorePattern: "^_",
41-
destructuredArrayIgnorePattern: "^_",
42-
varsIgnorePattern: "^_",
35+
args: 'all',
36+
argsIgnorePattern: '^_',
37+
caughtErrors: 'all',
38+
caughtErrorsIgnorePattern: '^_',
39+
destructuredArrayIgnorePattern: '^_',
40+
varsIgnorePattern: '^_',
4341
ignoreRestSiblings: true,
4442
},
4543
],

‎package.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
{
22
"name": "@openbytes/ts-react-directives",
3-
"version": "0.1.7",
3+
"version": "0.2.0",
44
"type": "module",
55
"author": {
66
"name": "SkyCodr (aka: Dulan Sudasinghe)",
77
"email": "dulan81@gmail.com"
88
},
9+
"license": "MIT",
910
"files": [
1011
"dist"
1112
],
13+
"main": "dist/ts-react-directives.cjs.js",
1214
"module": "dist/ts-react-directives.es.js",
1315
"types": "dist/ts-react-directives.d.ts",
1416
"exports": {
1517
".": {
16-
"import": "./dist/ts-react-directives.es.js"
18+
"import": "./dist/ts-react-directives.es.js",
19+
"require": "./dist/ts-react-directives.cjs.js"
1720
}
1821
},
1922
"scripts": {
@@ -29,7 +32,7 @@
2932
"test:coverage": "vitest --coverage",
3033
"prepare": "husky",
3134
"prepublishOnly": "pnpm run build",
32-
"publish:registry": "pnpm publish --access public"
35+
"publish:registry": "pnpm publish"
3336
},
3437
"dependencies": {
3538
"react": "^19.0.0",

‎src/__tests__/SwitchIf.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ describe('SwitchIf', () => {
110110
it('should render an error if, only ElseIf block is provided', () => {
111111
const { getByText } = render(
112112
<SwitchIf>
113-
<ElseIf>ElseIf Block</ElseIf>
113+
<ElseIf condition={true}>ElseIf Block</ElseIf>
114114
</SwitchIf>,
115115
);
116116

‎src/components/Errors.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import '@assets/index.css';
66
type ErrorProps = { errors: LogicErrors[] };
77

88
const Errors: FC<ErrorProps> = ({ errors }) => (
9-
<ol className="error-list">
9+
<ol className="trd-error-list">
1010
{errors.map((error, index) => (
1111
<li key={`${error}-${index}`}>{ERRORS[error]}</li>
1212
))}

‎src/directives/if/Else.tsx

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { FC } from 'react';
2-
import { useValidate } from '@hooks';
31
import { Errors } from '@components';
4-
import { ElseProps } from '@types';
2+
import { useValidate } from '@hooks';
3+
import { FC, PropsWithChildren } from 'react';
54

6-
const Else: FC<ElseProps> = (props) => {
7-
const errors = useValidate<ElseProps>(props, Else.name);
5+
export type ElseProps = {};
6+
7+
const Else: FC<PropsWithChildren<ElseProps>> = (props) => {
8+
const errors = useValidate(props, 'Else');
89
const children = errors.length === 0 ? props.children : <Errors errors={errors} />;
910

1011
return <>{children}</>;
1112
};
1213

14+
Else.displayName = 'Else';
15+
1316
export default Else;

‎src/directives/if/ElseIf.tsx

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1-
import { FC } from 'react';
2-
import { useValidate } from '@hooks';
31
import { Errors } from '@components';
4-
import { ElseIfProps } from '@types';
2+
import { useValidate } from '@hooks';
3+
import { FC, PropsWithChildren } from 'react';
4+
5+
export type ElseIfProps = PropsWithChildren<{
6+
condition: boolean;
7+
}>;
58

69
const ElseIf: FC<ElseIfProps> = (props) => {
7-
const errors = useValidate<ElseIfProps>(props, ElseIf.name);
10+
const errors = useValidate(props, 'ElseIf');
11+
const { condition } = props;
812
const children = errors.length === 0 ? props.children : <Errors errors={errors} />;
913

10-
return <>{children}</>;
14+
return <>{condition && children}</>;
1115
};
1216

17+
ElseIf.displayName = 'ElseIf';
18+
1319
export default ElseIf;

‎src/directives/if/If.tsx

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
import { FC } from 'react';
2-
import { useValidate } from '@hooks';
31
import { Errors } from '@components';
4-
import { IfProps } from '@types';
2+
import { useValidate } from '@hooks';
3+
4+
import { FC, PropsWithChildren } from 'react';
5+
6+
export type IfProps = PropsWithChildren<{
7+
condition: boolean;
8+
}>;
59

610
const If: FC<IfProps> = (props) => {
7-
const errors = useValidate<IfProps>(props, If.name);
11+
const errors = useValidate(props, 'If');
12+
const { condition } = props;
813
const children = errors.length === 0 ? props.children : <Errors errors={errors} />;
914

10-
return <>{children}</>;
15+
return <>{condition && children}</>;
1116
};
1217

18+
If.displayName = 'If';
19+
1320
export default If;

‎src/directives/if/SwitchIf.tsx

+37-34
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,62 @@
11
import { Errors } from '@components';
22
import { useValidate } from '@hooks';
3-
import { SwitchIfProps } from '@types';
4-
import { Children, createElement, FC, ReactElement } from 'react';
53

6-
const { log, warn } = console;
4+
import {
5+
Children,
6+
createElement,
7+
FC,
8+
FunctionComponent,
9+
isValidElement,
10+
NamedExoticComponent,
11+
PropsWithChildren,
12+
ReactElement,
13+
} from 'react';
714

8-
const useSwitchIf = (props: SwitchIfProps) => {
9-
const errors = useValidate<SwitchIfProps>(props, SwitchIf.name);
15+
export type SwitchIfProps = {};
16+
17+
const useSwitchIf = (props: PropsWithChildren) => {
18+
const errors = useValidate(props, 'SwitchIf');
1019

1120
if (errors.length) {
12-
warn('__ERRORS__', errors);
1321
return { children: createElement(Errors, { errors }) };
1422
}
1523

16-
const { children: oChildren } = props;
17-
const elements = Children.toArray(oChildren);
24+
const elements = Children.toArray(props.children);
25+
const validElements = elements.filter(isValidElement);
26+
const child = findChild(validElements);
1827

19-
let child = null;
28+
return { children: child };
29+
};
2030

21-
if (elements.length) {
22-
let i = 0;
23-
log('__FOUND_ELEMENTS__', elements);
24-
do {
25-
const { type } = (elements[i] ?? {}) as ReactElement;
31+
const findChild = (elements: ReactElement[]) => {
32+
for (const element of elements) {
33+
if (!element || !isValidElement(element)) {
34+
continue;
35+
}
2636

27-
log('__INSIDE_LOOP__', i, type);
37+
const { displayName } = element.type as NamedExoticComponent | FunctionComponent;
2838

29-
if (type) {
30-
const { name } = type as { name: string };
31-
const { condition } = ((elements[i] as ReactElement)?.props ?? {}) as { condition: boolean };
39+
if (displayName) {
40+
const { condition } = element.props as { condition: boolean };
3241

33-
if ((name === 'If' || name === 'ElseIf') && condition === true) {
34-
log('__ELEMENT_FOUND_1__', name);
35-
child = elements[i];
36-
break;
37-
}
42+
if ((displayName === 'If' || displayName === 'ElseIf') && condition === true) {
43+
return element;
44+
}
3845

39-
if (name === 'Else') {
40-
log('__ELEMENT_FOUND_2__', name);
41-
child = elements[i];
42-
break;
43-
}
46+
if (displayName === 'Else') {
47+
return element;
4448
}
45-
i++;
46-
} while (i < elements.length);
49+
}
4750
}
4851

49-
log('__CHILD__', child);
50-
51-
return { children: child };
52+
return null;
5253
};
5354

54-
const SwitchIf: FC<SwitchIfProps> = (props) => {
55+
const SwitchIf: FC<PropsWithChildren<SwitchIfProps>> = (props) => {
5556
const { children } = useSwitchIf(props);
5657
return <>{children}</>;
5758
};
5859

60+
SwitchIf.displayName = 'SwitchIf';
61+
5962
export default SwitchIf;

‎src/directives/if/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { default as SwitchIf } from './SwitchIf';
2-
export { default as If } from './If';
3-
export { default as ElseIf } from './ElseIf';
4-
export { default as Else } from './Else';
1+
export { default as SwitchIf, type SwitchIfProps } from './SwitchIf';
2+
export { default as If, type IfProps } from './If';
3+
export { default as ElseIf, type ElseIfProps } from './ElseIf';
4+
export { default as Else, type ElseProps } from './Else';

‎src/examples/App.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ function App() {
2222
<div>ElseIf value = 1</div>
2323
<SwitchIf>
2424
<If condition={val2}>
25-
<div>Sub If value = true</div>
25+
<div style={{ color: 'blue' }}>Sub If value = true</div>
2626
</If>
2727
<Else>
28-
<div>Sub Else value = false</div>
28+
<div style={{ color: 'red' }}>Sub Else value = false</div>
2929
</Else>
3030
</SwitchIf>
3131
</ElseIf>
@@ -35,6 +35,9 @@ function App() {
3535
<Else>
3636
<div>Else (fall through {val} )</div>
3737
</Else>
38+
<Else>
39+
<div>Else (fall through {val} )</div>
40+
</Else>
3841
</SwitchIf>
3942
</div>
4043
);

‎src/hooks/useValidate.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Children, PropsWithChildren, ReactNode } from 'react';
21
import { ValidationFactory } from '@utils';
2+
import { Children, PropsWithChildren } from 'react';
33

4-
export const useValidate = <T extends ReactNode>(props: PropsWithChildren<T>, name: string) => {
4+
export const useValidate = (props: PropsWithChildren, name: string) => {
55
const { children } = props;
66
const _children = Children.toArray(children);
77

‎src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { Else, ElseIf, If, SwitchIf } from '@directives';
1+
export { Else, ElseIf, If, SwitchIf, type IfProps, type ElseIfProps } from '@directives';

‎src/types/index.ts

-6
This file was deleted.

‎src/utils/validators.ts

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Else, ElseIf, If, SwitchIf } from '@directives';
22
import { LogicErrors } from '@fixtures';
33

4-
import { isValidElement, ReactNode } from 'react';
4+
import { ReactNode } from 'react';
55

66
type ValidatorFn = (children: Array<Exclude<ReactNode, boolean | null | undefined>>) => number[];
77

@@ -14,11 +14,11 @@ const validateSwitchIfChildren: ValidatorFn = (children) => {
1414
}
1515

1616
children.forEach((child) => {
17-
// @ts-expect-error type.name exists on the child
18-
const typeName = isValidElement(child) ? child.type.name : 'unknown';
17+
// @ts-expect-error child.type exist
18+
const { displayName } = child.type ?? {};
1919

2020
// If, ElseIf, Else cannot be direct children of If, ElseIf, Else
21-
if (typeName === If.name || typeName === ElseIf.name || typeName === Else.name) {
21+
if (displayName === If.displayName || displayName === ElseIf.displayName || displayName === Else.displayName) {
2222
errors.push(LogicErrors.SwitchBlockExpected);
2323
}
2424
});
@@ -28,27 +28,27 @@ const validateSwitchIfChildren: ValidatorFn = (children) => {
2828

2929
const validateSwitchIf: ValidatorFn = (children) => {
3030
const errors: LogicErrors[] = [];
31-
const elements: Record<string, number> = {};
31+
const elementLookup: Record<string, number> = {};
3232

3333
if (children.length === 0) {
3434
errors.push(LogicErrors.ChildrenExpected, LogicErrors.IfBlockExpected);
3535
return errors;
3636
}
3737

3838
children.forEach((child, index) => {
39-
// @ts-expect-error type.name exists on the child
40-
const typeName = isValidElement(child) ? child.type.name : 'unknown';
39+
// @ts-expect-error child.type exist
40+
const { displayName = 'unknown' } = child.type;
4141

42-
const count = elements[typeName] ?? 0;
43-
elements[typeName] = count + 1;
42+
const count = elementLookup[displayName] ?? 0;
43+
elementLookup[displayName] = count + 1;
4444

45-
validateIfBlock(typeName, index, elements, errors);
46-
validateElseBlock(typeName, index, children.length, elements, errors);
47-
validateElseIfBlock(typeName, index, errors);
48-
validateSwitchIfInvalidElement(typeName, errors);
45+
validateIfBlock(displayName, index, elementLookup, errors);
46+
validateElseBlock(displayName, index, children.length, elementLookup, errors);
47+
validateElseIfBlock(displayName, index, errors);
48+
validateSwitchIfInvalidElement(displayName, errors);
4949
});
5050

51-
if (!elements[If.name]) {
51+
if (!elementLookup[If.name]) {
5252
errors.push(LogicErrors.IfBlockExpected);
5353
}
5454

@@ -100,12 +100,12 @@ export class ValidationFactory {
100100
static get(validator: string) {
101101
let validatorFn: ValidatorFn;
102102
switch (validator) {
103-
case SwitchIf.name:
103+
case SwitchIf.displayName:
104104
validatorFn = validateSwitchIf;
105105
break;
106-
case If.name:
107-
case ElseIf.name:
108-
case Else.name:
106+
case If.displayName:
107+
case ElseIf.displayName:
108+
case Else.displayName:
109109
validatorFn = validateSwitchIfChildren;
110110
break;
111111
default:

‎tsconfig.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
"@directives": ["src/directives"],
1616
"@fixtures": ["src/fixtures"],
1717
"@hooks": ["src/hooks"],
18-
"@utils": ["src/utils"],
19-
"@types": ["src/types"]
18+
"@utils": ["src/utils"]
2019
},
2120

2221
/* Bundler mode */

‎vite.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default defineConfig({
2525
lib: {
2626
entry: path.resolve(__dirname, 'src/index.ts'),
2727
name: libraryName,
28-
formats: ['es'],
28+
formats: ['es', 'cjs', 'umd'],
2929
fileName: (format) => `${libraryName}.${format}.js`,
3030
},
3131

‎vitest.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default defineConfig((_configEnv) =>
1515
coverage: {
1616
reportsDirectory: './reports/coverage',
1717
include: ['src/**/*.{ts,tsx}'],
18-
exclude: ['src/__tests__/**', 'src/types', '*.d.ts'],
18+
exclude: ['src/__tests__/**', '**/*.d.ts', 'src/index.ts', 'src/main.tsx', 'src/examples/**'],
1919
},
2020
},
2121
}),

0 commit comments

Comments
 (0)
Please sign in to comment.