Skip to content

Commit

Permalink
feat(utils): move checkPropTypes to utils module
Browse files Browse the repository at this point in the history
  • Loading branch information
liujuping committed Jan 25, 2024
1 parent cd67a8c commit 9edecf1
Show file tree
Hide file tree
Showing 13 changed files with 305 additions and 299 deletions.
3 changes: 1 addition & 2 deletions packages/renderer-core/src/renderer/base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import classnames from 'classnames';
import { create as createDataSourceEngine } from '@alilc/lowcode-datasource-engine/interpret';
import { IPublicTypeNodeSchema, IPublicTypeNodeData, IPublicTypeJSONValue, IPublicTypeCompositeValue } from '@alilc/lowcode-types';
import { isI18nData, isJSExpression, isJSFunction } from '@alilc/lowcode-utils';
import { checkPropTypes, isI18nData, isJSExpression, isJSFunction } from '@alilc/lowcode-utils';
import adapter from '../adapter';
import divFactory from '../components/Div';
import visualDomFactory from '../components/VisualDom';
Expand All @@ -21,7 +21,6 @@ import {
isFileSchema,
transformArrayToMap,
transformStringToFunction,
checkPropTypes,
getI18n,
getFileCssName,
capitalizeFirstLetter,
Expand Down
76 changes: 0 additions & 76 deletions packages/renderer-core/src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,11 @@ import { isI18nData, isJSExpression } from '@alilc/lowcode-utils';
import { isEmpty } from 'lodash';
import IntlMessageFormat from 'intl-messageformat';
import pkg from '../../package.json';
import * as ReactIs from 'react-is';
import { default as ReactPropTypesSecret } from 'prop-types/lib/ReactPropTypesSecret';
import { default as factoryWithTypeCheckers } from 'prop-types/factoryWithTypeCheckers';

(window as any).sdkVersion = pkg.version;

export { pick, isEqualWith as deepEqual, cloneDeep as clone, isEmpty, throttle, debounce } from 'lodash';

const PropTypes2 = factoryWithTypeCheckers(ReactIs.isElement, true);

const EXPRESSION_TYPE = {
JSEXPRESSION: 'JSExpression',
JSFUNCTION: 'JSFunction',
Expand Down Expand Up @@ -183,77 +178,6 @@ export function transformArrayToMap(arr: any[], key: string, overwrite = true) {
return res;
}

export function isBasicType(propType: IPublicTypePropType): propType is IPublicTypeBasicType {
if (!propType) {
return false;
}
return typeof propType === 'string';
}

export function isRequiredType(propType: IPublicTypePropType): propType is IPublicTypeRequiredType {
if (!propType) {
return false;
}
return typeof propType === 'object' && propType.type && ['array', 'bool', 'func', 'number', 'object', 'string', 'node', 'element', 'any'].includes(propType.type);
}

export function transformPropTypesRuleToString(rule: IPublicTypePropType): string {
if (!rule) {
return 'PropTypes.any';
}

if (typeof rule === 'string') {
return `PropTypes.${rule}`;
}

if (isRequiredType(rule)) {
const { type, isRequired } = rule;
return `PropTypes.${type}${isRequired ? '.isRequired' : ''}`;
}

const { type, value } = rule;
switch (type) {
case 'oneOf':
return `PropTypes.oneOf([${value.map((item: any) => `"${item}"`).join(',')}])`;
case 'oneOfType':
return `PropTypes.oneOfType([${value.map((item: any) => transformPropTypesRuleToString(item)).join(', ')}])`;
case 'arrayOf':
case 'objectOf':
return `PropTypes.${type}(${transformPropTypesRuleToString(value)})`;
case 'shape':
case 'exact':
return `PropTypes.${type}({${value.map((item: any) => `${item.name}: ${transformPropTypesRuleToString(item.propType)}`).join(',')}})`;
}
}

export function checkPropTypes(value: any, name: string, rule: any, componentName: string): boolean {
let ruleFunction = rule;
if (typeof rule === 'object') {
ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${transformPropTypesRuleToString(rule)}`)(PropTypes2);
}
if (typeof rule === 'string') {
ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${rule}`)(PropTypes2);
}
if (!ruleFunction || typeof ruleFunction !== 'function') {
logger.warn('checkPropTypes should have a function type rule argument');
return true;
}
const err = ruleFunction(
{
[name]: value,
},
name,
componentName,
'prop',
null,
ReactPropTypesSecret,
);
if (err) {
logger.warn(err);
}
return !err;
}

/**
* transform string to a function
* @param str function in string form
Expand Down
219 changes: 0 additions & 219 deletions packages/renderer-core/tests/utils/common.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import factoryWithTypeCheckers from 'prop-types/factoryWithTypeCheckers';
import {
isSchema,
isFileSchema,
Expand All @@ -18,17 +17,9 @@ import {
parseThisRequiredExpression,
parseI18n,
parseData,
checkPropTypes,
transformPropTypesRuleToString,
isRequiredType,
isBasicType,
} from '../../src/utils/common';
import logger from '../../src/utils/logger';

var ReactIs = require('react-is');

const PropTypes = factoryWithTypeCheckers(ReactIs.isElement, true);

describe('test isSchema', () => {
it('should be false when empty value is passed', () => {
expect(isSchema(null)).toBeFalsy();
Expand Down Expand Up @@ -470,213 +461,3 @@ describe('test parseData ', () => {

});
});

describe('test isBasicType ', () => {
it('should work', () => {
expect(isBasicType(null)).toBeFalsy();
expect(isBasicType(undefined)).toBeFalsy();
expect(isBasicType({})).toBeFalsy();
expect(isBasicType({ type: 'any other type' })).toBeFalsy();
expect(isBasicType('string')).toBeTruthy();
});
});

describe('test isRequiredType', () => {
it('should work', () => {
expect(isRequiredType(null)).toBeFalsy();
expect(isRequiredType(undefined)).toBeFalsy();
expect(isRequiredType({})).toBeFalsy();
expect(isRequiredType({ type: 'any other type' })).toBeFalsy();
expect(isRequiredType('string')).toBeFalsy();
expect(isRequiredType({ type: 'string' })).toBeTruthy();
expect(isRequiredType({ type: 'string', isRequired: true })).toBeTruthy();
});
})

describe('checkPropTypes', () => {
it('should validate correctly with valid prop type', () => {
expect(checkPropTypes(123, 'age', PropTypes.number, 'TestComponent')).toBe(true);
expect(checkPropTypes('123', 'age', PropTypes.string, 'TestComponent')).toBe(true);
});

it('should log a warning and return false with invalid prop type', () => {
expect(checkPropTypes(123, 'age', PropTypes.string, 'TestComponent')).toBe(false);
expect(checkPropTypes('123', 'age', PropTypes.number, 'TestComponent')).toBe(false);
});

it('should handle custom rule functions correctly', () => {
const customRule = (props, propName) => {
if (props[propName] !== 123) {
return new Error('Invalid value');
}
};
const result = checkPropTypes(123, 'customProp', customRule, 'TestComponent');
expect(result).toBe(true);
});


it('should interpret and validate a rule given as a string', () => {
const result = checkPropTypes(123, 'age', 'PropTypes.number', 'TestComponent');
expect(result).toBe(true);
});

it('should log a warning for invalid rule type', () => {
const result = checkPropTypes(123, 'age', 123, 'TestComponent');
expect(result).toBe(true);
});

// oneOf
it('should validate correctly with valid oneOf prop type', () => {
const rule = {
type: 'oneOf',
value: ['News', 'Photos'],
}
expect(transformPropTypesRuleToString(rule)).toBe(`PropTypes.oneOf(["News","Photos"])`);
expect(checkPropTypes('News', 'type', rule, 'TestComponent')).toBe(true);
expect(checkPropTypes('Others', 'type', rule, 'TestComponent')).toBe(false);
});

// oneOfType
it('should validate correctly with valid oneOfType prop type', () => {
const rule = {
type: 'oneOfType',
value: ['string', 'number', {
type: 'array',
isRequired: true,
}],
};
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array.isRequired])');
expect(checkPropTypes(['News', 'Photos'], 'type', rule, 'TestComponent')).toBe(true);
expect(checkPropTypes('News', 'type', rule, 'TestComponent')).toBe(true);
expect(checkPropTypes(123, 'type', rule, 'TestComponent')).toBe(true);
expect(checkPropTypes({}, 'type', rule, 'TestComponent')).toBe(false);
});

// arrayOf
it('should validate correctly with valid arrayOf prop type', () => {
const rule = {
type: 'arrayOf',
value: {
type: 'string',
isRequired: true,
},
};
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.arrayOf(PropTypes.string.isRequired)');
expect(checkPropTypes(['News', 'Photos'], 'type', rule, 'TestComponent')).toBe(true);
expect(checkPropTypes(['News', 123], 'type', rule, 'TestComponent')).toBe(false);
});

// objectOf
it('should validate correctly with valid objectOf prop type', () => {
const rule = {
type: 'objectOf',
value: {
type: 'string',
isRequired: true,
},
};
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.objectOf(PropTypes.string.isRequired)');
expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(true);
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(false);
});

// shape
it('should validate correctly with valid shape prop type', () => {
const rule = {
type: 'shape',
value: [
{
name: 'a',
propType: {
type: 'string',
isRequired: true,
},
},
{
name: 'b',
propType: {
type: 'number',
isRequired: true,
},
},
],
};
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.shape({a: PropTypes.string.isRequired,b: PropTypes.number.isRequired})');
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(true);
expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(false);

// isRequired
const rule2 = {
type: 'shape',
value: [
{
name: 'a',
propType: {
type: 'string',
isRequired: true,
},
},
{
name: 'b',
propType: {
type: 'number',
isRequired: false,
},
},
],
};
expect(transformPropTypesRuleToString(rule2)).toBe('PropTypes.shape({a: PropTypes.string.isRequired,b: PropTypes.number})');
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule2, 'TestComponent')).toBe(true);
expect(checkPropTypes({ b: 123 }, 'type', rule2, 'TestComponent')).toBe(false);
});

// exact
it('should validate correctly with valid exact prop type', () => {
const rule = {
type: 'exact',
value: [
{
name: 'a',
propType: {
type: 'string',
isRequired: true,
},
},
{
name: 'b',
propType: {
type: 'number',
isRequired: true,
},
},
],
};
expect(transformPropTypesRuleToString(rule)).toBe('PropTypes.exact({a: PropTypes.string.isRequired,b: PropTypes.number.isRequired})');
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule, 'TestComponent')).toBe(true);
expect(checkPropTypes({ a: 'News', b: 'Photos' }, 'type', rule, 'TestComponent')).toBe(false);

// isRequired
const rule2 = {
type: 'exact',
value: [
{
name: 'a',
propType: {
type: 'string',
isRequired: true,
},
},
{
name: 'b',
propType: {
type: 'number',
isRequired: false,
},
},
],
};
expect(transformPropTypesRuleToString(rule2)).toBe('PropTypes.exact({a: PropTypes.string.isRequired,b: PropTypes.number})');
expect(checkPropTypes({ a: 'News', b: 123 }, 'type', rule2, 'TestComponent')).toBe(true);
expect(checkPropTypes({ b: 123 }, 'type', rule2, 'TestComponent')).toBe(false);
});
});
1 change: 1 addition & 0 deletions packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@alilc/lowcode-types": "1.3.1",
"lodash": "^4.17.21",
"mobx": "^6.3.0",
"prop-types": "^15.8.1",
"react": "^16"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit 9edecf1

Please sign in to comment.