Skip to content

Commit e6ba79b

Browse files
liujupingJackLian
authored andcommitted
feat(utils): checkPropTypes suport IPublicTypePropType as rule for check
1 parent de738b5 commit e6ba79b

2 files changed

Lines changed: 229 additions & 3 deletions

File tree

packages/renderer-core/src/utils/common.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable no-console */
22
/* eslint-disable no-new-func */
33
import logger from './logger';
4-
import { IPublicTypeRootSchema, IPublicTypeNodeSchema, IPublicTypeJSSlot } from '@alilc/lowcode-types';
4+
import { IPublicTypeRootSchema, IPublicTypeNodeSchema, IPublicTypeJSSlot, IPublicTypePropType, IPublicTypeRequiredType, IPublicTypeBasicType } from '@alilc/lowcode-types';
55
import { isI18nData, isJSExpression } from '@alilc/lowcode-utils';
66
import { isEmpty } from 'lodash';
77
import IntlMessageFormat from 'intl-messageformat';
@@ -183,8 +183,54 @@ export function transformArrayToMap(arr: any[], key: string, overwrite = true) {
183183
return res;
184184
}
185185

186+
export function isBasicType(propType: IPublicTypePropType): propType is IPublicTypeBasicType {
187+
if (!propType) {
188+
return false;
189+
}
190+
return typeof propType === 'string';
191+
}
192+
193+
export function isRequiredType(propType: IPublicTypePropType): propType is IPublicTypeRequiredType {
194+
if (!propType) {
195+
return false;
196+
}
197+
return typeof propType === 'object' && propType.type && ['array', 'bool', 'func', 'number', 'object', 'string', 'node', 'element', 'any'].includes(propType.type);
198+
}
199+
200+
export function transformPropTypesRuleToString(rule: IPublicTypePropType): string {
201+
if (!rule) {
202+
return 'PropTypes.any';
203+
}
204+
205+
if (typeof rule === 'string') {
206+
return `PropTypes.${rule}`;
207+
}
208+
209+
if (isRequiredType(rule)) {
210+
const { type, isRequired } = rule;
211+
return `PropTypes.${type}${isRequired ? '.isRequired' : ''}`;
212+
}
213+
214+
const { type, value } = rule;
215+
switch (type) {
216+
case 'oneOf':
217+
return `PropTypes.oneOf([${value.map((item: any) => `"${item}"`).join(',')}])`;
218+
case 'oneOfType':
219+
return `PropTypes.oneOfType([${value.map((item: any) => transformPropTypesRuleToString(item)).join(', ')}])`;
220+
case 'arrayOf':
221+
case 'objectOf':
222+
return `PropTypes.${type}(${transformPropTypesRuleToString(value)})`;
223+
case 'shape':
224+
case 'exact':
225+
return `PropTypes.${type}({${value.map((item: any) => `${item.name}: ${transformPropTypesRuleToString(item.propType)}`).join(',')}})`;
226+
}
227+
}
228+
186229
export function checkPropTypes(value: any, name: string, rule: any, componentName: string): boolean {
187230
let ruleFunction = rule;
231+
if (typeof rule === 'object') {
232+
ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${transformPropTypesRuleToString(rule)}`)(PropTypes2);
233+
}
188234
if (typeof rule === 'string') {
189235
ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${rule}`)(PropTypes2);
190236
}

packages/renderer-core/tests/utils/common.test.ts

Lines changed: 182 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import {
1919
parseI18n,
2020
parseData,
2121
checkPropTypes,
22+
transformPropTypesRuleToString,
23+
isRequiredType,
24+
isBasicType,
2225
} from '../../src/utils/common';
2326
import logger from '../../src/utils/logger';
2427

@@ -283,7 +286,7 @@ describe('test capitalizeFirstLetter ', () => {
283286
describe('test forEach ', () => {
284287
it('should work', () => {
285288
const mockFn = jest.fn();
286-
289+
287290
forEach(null, mockFn);
288291
expect(mockFn).toBeCalledTimes(0);
289292

@@ -298,7 +301,7 @@ describe('test forEach ', () => {
298301

299302
forEach({ a: 1, b: 2, c: 3 }, mockFn);
300303
expect(mockFn).toBeCalledTimes(3);
301-
304+
302305
const mockFn2 = jest.fn();
303306
forEach({ a: 1 }, mockFn2, { b: 'bbb' });
304307
expect(mockFn2).toHaveBeenCalledWith(1, 'a');
@@ -468,6 +471,28 @@ describe('test parseData ', () => {
468471
});
469472
});
470473

474+
describe('test isBasicType ', () => {
475+
it('should work', () => {
476+
expect(isBasicType(null)).toBeFalsy();
477+
expect(isBasicType(undefined)).toBeFalsy();
478+
expect(isBasicType({})).toBeFalsy();
479+
expect(isBasicType({ type: 'any other type' })).toBeFalsy();
480+
expect(isBasicType('string')).toBeTruthy();
481+
});
482+
});
483+
484+
describe('test isRequiredType', () => {
485+
it('should work', () => {
486+
expect(isRequiredType(null)).toBeFalsy();
487+
expect(isRequiredType(undefined)).toBeFalsy();
488+
expect(isRequiredType({})).toBeFalsy();
489+
expect(isRequiredType({ type: 'any other type' })).toBeFalsy();
490+
expect(isRequiredType('string')).toBeFalsy();
491+
expect(isRequiredType({ type: 'string' })).toBeTruthy();
492+
expect(isRequiredType({ type: 'string', isRequired: true })).toBeTruthy();
493+
});
494+
})
495+
471496
describe('checkPropTypes', () => {
472497
it('should validate correctly with valid prop type', () => {
473498
expect(checkPropTypes(123, 'age', PropTypes.number, 'TestComponent')).toBe(true);
@@ -499,4 +524,159 @@ describe('checkPropTypes', () => {
499524
const result = checkPropTypes(123, 'age', 123, 'TestComponent');
500525
expect(result).toBe(true);
501526
});
527+
528+
// oneOf
529+
it('should validate correctly with valid oneOf prop type', () => {
530+
const rule = {
531+
type: 'oneOf',
532+
value: ['News', 'Photos'],
533+
}
534+
expect(transformPropTypesRuleToString(rule)).toBe(`PropTypes.oneOf(["News","Photos"])`);
535+
expect(checkPropTypes('News', 'type', rule, 'TestComponent')).toBe(true);
536+
expect(checkPropTypes('Others', 'type', rule, 'TestComponent')).toBe(false);
537+
});
538+
539+
// oneOfType
540+
it('should validate correctly with valid oneOfType prop type', () => {
541+
const rule = {
542+
type: 'oneOfType',
543+
value: ['string', 'number', {
544+
type: 'array',
545+
isRequired: true,
546+
}],
547+
};
548+
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array.isRequired])');
549+
expect(checkPropTypes(['News', 'Photos'], 'type', rule, 'TestComponent')).toBe(true);
550+
expect(checkPropTypes('News', 'type', rule, 'TestComponent')).toBe(true);
551+
expect(checkPropTypes(123, 'type', rule, 'TestComponent')).toBe(true);
552+
expect(checkPropTypes({}, 'type', rule, 'TestComponent')).toBe(false);
553+
});
554+
555+
// arrayOf
556+
it('should validate correctly with valid arrayOf prop type', () => {
557+
const rule = {
558+
type: 'arrayOf',
559+
value: {
560+
type: 'string',
561+
isRequired: true,
562+
},
563+
};
564+
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.arrayOf(PropTypes.string.isRequired)');
565+
expect(checkPropTypes(['News', 'Photos'], 'type', rule, 'TestComponent')).toBe(true);
566+
expect(checkPropTypes(['News', 123], 'type', rule, 'TestComponent')).toBe(false);
567+
});
568+
569+
// objectOf
570+
it('should validate correctly with valid objectOf prop type', () => {
571+
const rule = {
572+
type: 'objectOf',
573+
value: {
574+
type: 'string',
575+
isRequired: true,
576+
},
577+
};
578+
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.objectOf(PropTypes.string.isRequired)');
579+
expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(true);
580+
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(false);
581+
});
582+
583+
// shape
584+
it('should validate correctly with valid shape prop type', () => {
585+
const rule = {
586+
type: 'shape',
587+
value: [
588+
{
589+
name: 'a',
590+
propType: {
591+
type: 'string',
592+
isRequired: true,
593+
},
594+
},
595+
{
596+
name: 'b',
597+
propType: {
598+
type: 'number',
599+
isRequired: true,
600+
},
601+
},
602+
],
603+
};
604+
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.shape({a: PropTypes.string.isRequired,b: PropTypes.number.isRequired})');
605+
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(true);
606+
expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(false);
607+
608+
// isRequired
609+
const rule2 = {
610+
type: 'shape',
611+
value: [
612+
{
613+
name: 'a',
614+
propType: {
615+
type: 'string',
616+
isRequired: true,
617+
},
618+
},
619+
{
620+
name: 'b',
621+
propType: {
622+
type: 'number',
623+
isRequired: false,
624+
},
625+
},
626+
],
627+
};
628+
expect(transformPropTypesRuleToString(rule2)).toBe('PropTypes.shape({a: PropTypes.string.isRequired,b: PropTypes.number})');
629+
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule2, 'TestComponent')).toBe(true);
630+
expect(checkPropTypes({ b: 123 }, 'type', rule2, 'TestComponent')).toBe(false);
631+
});
632+
633+
// exact
634+
it('should validate correctly with valid exact prop type', () => {
635+
const rule = {
636+
type: 'exact',
637+
value: [
638+
{
639+
name: 'a',
640+
propType: {
641+
type: 'string',
642+
isRequired: true,
643+
},
644+
},
645+
{
646+
name: 'b',
647+
propType: {
648+
type: 'number',
649+
isRequired: true,
650+
},
651+
},
652+
],
653+
};
654+
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.exact({a: PropTypes.string.isRequired,b: PropTypes.number.isRequired})');
655+
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(true);
656+
expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(false);
657+
658+
// isRequired
659+
const rule2 = {
660+
type: 'exact',
661+
value: [
662+
{
663+
name: 'a',
664+
propType: {
665+
type: 'string',
666+
isRequired: true,
667+
},
668+
},
669+
{
670+
name: 'b',
671+
propType: {
672+
type: 'number',
673+
isRequired: false,
674+
},
675+
},
676+
],
677+
};
678+
expect(transformPropTypesRuleToString(rule2)).toBe('PropTypes.exact({a: PropTypes.string.isRequired,b: PropTypes.number})');
679+
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule2, 'TestComponent')).toBe(true);
680+
expect(checkPropTypes({ b: 123 }, 'type', rule2, 'TestComponent')).toBe(false);
681+
});
502682
});

0 commit comments

Comments
 (0)