From 4e77d7a886dcf47af9e5f47c0d63f742bec772b1 Mon Sep 17 00:00:00 2001 From: Wells Date: Thu, 16 May 2024 11:28:45 +0800 Subject: [PATCH] feat: improve code2value function and fix related issues --- packages/core/src/helpers/code-helpers.ts | 19 +++--- packages/core/tests/ast.test.ts | 3 +- packages/core/tests/helpers.test.ts | 16 +++-- .../variable-tree/service-preview.tsx | 5 +- packages/helpers/src/helpers/code-helper.ts | 65 ------------------- packages/helpers/tests/helpers.test.ts | 28 -------- packages/setting-form/src/form-item.tsx | 6 +- 7 files changed, 27 insertions(+), 115 deletions(-) diff --git a/packages/core/src/helpers/code-helpers.ts b/packages/core/src/helpers/code-helpers.ts index 8b5cfd74..b0f6ce2d 100644 --- a/packages/core/src/helpers/code-helpers.ts +++ b/packages/core/src/helpers/code-helpers.ts @@ -49,15 +49,18 @@ export function value2code(val: any) { export const value2expressionCode = value2code; /** - * 代码字符串转为 js value - * TODO: 暂时还没有使用,需要测试充分 - * FIXME: expect(code2value('{ foo: "foo", ...{ bar: "bar"} }')).toEqual({ foo: 'foo', bar: 'bar' }); + * 代码字符串转为具体的 js value + * @example `() => {}` 返回 undefined + * + * @param rawCode 代码字符串 + * @returns 返回解析后的 js value,包括:string, number, boolean, simpleObject, simpleArray */ -export function code2value(code: string) { - if (isWrappedCode(code)) { - code = getCodeOfWrappedCode(code); - } - const node = code2expression(code); +export function code2value(rawCode: string) { + const node = code2expression(rawCode); const value = node2value(node); + if (isWrappedCode(value)) { + // 能转的就转,转不能的就返回空 + return; + } return value; } diff --git a/packages/core/tests/ast.test.ts b/packages/core/tests/ast.test.ts index 0feb843c..38f58065 100644 --- a/packages/core/tests/ast.test.ts +++ b/packages/core/tests/ast.test.ts @@ -54,8 +54,7 @@ describe('ast helpers', () => { it('code2expression', () => { expect(code2expression('')).toBeUndefined(); - expect(code2expression('{tango.stores.app}')).toBeUndefined(); - + expect(code2expression('tango.stores.app').type).toBe('MemberExpression'); expect(code2expression('{ type: window.bar }').type).toEqual('ObjectExpression'); expect(code2expression('() => {};').type).toEqual('ArrowFunctionExpression'); expect(code2expression('').type).toBe('JSXElement'); diff --git a/packages/core/tests/helpers.test.ts b/packages/core/tests/helpers.test.ts index ecd54d83..2a1405ed 100644 --- a/packages/core/tests/helpers.test.ts +++ b/packages/core/tests/helpers.test.ts @@ -223,11 +223,15 @@ describe('schema helpers', () => { describe('code helper', () => { it('code2value', () => { - expect(code2value('1')).toBe(1); - expect(code2value('{{1}}')).toBe(1); - expect(code2value('{{false}}')).toEqual(false); - expect(code2value('{{"foo"}}')).toBe('foo'); - // TODO: 这种情况需要考虑下 - // expect(code2value('{ foo: "foo", ...{ bar: "bar"} }')).toBe('foo'); + expect(code2value(`1`)).toEqual(1); + expect(code2value(`false`)).toEqual(false); + expect(code2value(`"foo"`)).toEqual('foo'); + expect(code2value(`{ foo: "foo" }`)).toEqual({ foo: 'foo' }); + expect(code2value(`[{ foo: "foo" }]`)).toEqual([{ foo: 'foo' }]); + expect(code2value(`{ foo: "foo", ...{ bar: "bar"} }`)).toBe(undefined); + expect(code2value(`() => {}`)).toBe(undefined); + expect(code2value(`tango.stores.app.name`)).toBe(undefined); + expect(code2value(`window`)).toBe(undefined); + expect(code2value(`
hello
`)).toBe(undefined); }); }); diff --git a/packages/designer/src/components/variable-tree/service-preview.tsx b/packages/designer/src/components/variable-tree/service-preview.tsx index ddef7b17..ac89aed0 100644 --- a/packages/designer/src/components/variable-tree/service-preview.tsx +++ b/packages/designer/src/components/variable-tree/service-preview.tsx @@ -3,7 +3,8 @@ import { Box } from 'coral-system'; import { Button, Empty } from 'antd'; import { PlayCircleOutlined } from '@ant-design/icons'; import { InputCode, Panel, JsonView } from '@music163/tango-ui'; -import { isNil, logger, code2object, getValue } from '@music163/tango-helpers'; +import { isNil, logger, getValue } from '@music163/tango-helpers'; +import { code2value } from '@music163/tango-core'; export interface ServicePreviewProps { appContext?: any; @@ -22,7 +23,7 @@ export function ServicePreview({ appContext, functionKey }: ServicePreviewProps) editable showLineNumbers onChange={(value: string) => { - const obj = code2object(value); + const obj = code2value(value); // 转为 object 对象 setPayload(obj); }} /> diff --git a/packages/helpers/src/helpers/code-helper.ts b/packages/helpers/src/helpers/code-helper.ts index 0be74ba5..0fcb6083 100644 --- a/packages/helpers/src/helpers/code-helper.ts +++ b/packages/helpers/src/helpers/code-helper.ts @@ -32,24 +32,6 @@ export function isValidFunctionCode(str: string) { } } -/** - * 是否是有效的对象字符串 - * - * @example { foo: 'foo' } - * @example [{ foo: 'foo' }] - * TODO: 考虑箭头函数的情况 () => {} - * - * @param str - * @returns - */ -export function isValidObjectString(str: string) { - const obj = code2object(str); - if (obj && typeof obj === 'object') { - return true; - } - return false; -} - const templatePattern = /^{{(.+)}}$/s; /** @@ -119,53 +101,6 @@ export function isPlainString(str: string) { return isString && !isWrapped; } -// 提供给代码执行环境的全局变量 -const patchCode = ` -var tango = { - stores: {}, - services: {}, - config: {}, - refs: {}, -}; -`; - -/** - * 将代码放到函数体中进行执行 - * @param code - * @returns 函数执行的结果 - */ -export function runCode(code: string) { - let ret; - try { - // eslint-disable-next-line no-new-func - ret = new Function(`${patchCode}\n return ${code}`)(); - } catch (err) { - // ignore error - } - return ret; -} - -// eslint-disable-next-line no-useless-escape -const objectWrapperPattern = /^[{\[].*[}\]]$/s; - -/** - * 将代码片段转成 js 对象 - * FIXME: 这个逻辑有问题,不严谨 - * @warning 不严谨,待优化 - * @param code 代码文本 - * @param isStrict 是否为严格模式(是否废弃) - * @returns - */ -export function code2object(code: string, isStrict = true) { - // 非严格模式直接执行 - // 严格模式下需检测 code 是一个对象 - if (!isStrict || (isStrict && objectWrapperPattern.test(code))) { - const ret = runCode(code); - return typeof ret === 'object' ? ret : undefined; - } - return code; -} - const codeBlockPattern = /```(\w*)([\s\S]*?)```/g; /** diff --git a/packages/helpers/tests/helpers.test.ts b/packages/helpers/tests/helpers.test.ts index 61788024..7d44a981 100644 --- a/packages/helpers/tests/helpers.test.ts +++ b/packages/helpers/tests/helpers.test.ts @@ -1,7 +1,5 @@ import { isVariableString, - isValidObjectString, - code2object, parseDndTrackId, getVariableContent, camelCase, @@ -9,7 +7,6 @@ import { isValidUrl, getCodeBlockFormMarkdown, upperCamelCase, - runCode, } from '../src/helpers'; describe('string', () => { @@ -54,24 +51,6 @@ describe('string', () => { expect(getVariableContent('{{!false}}')).toBe('!false'); }); - it('isValidObjectString', () => { - expect(isValidObjectString('{ foo: "bar" }')).toBeTruthy(); - expect(isValidObjectString('[{ foo: "bar" }]')).toBeTruthy(); - expect(isValidObjectString('[1,2,3]')).toBeTruthy(); - expect(isValidObjectString('["hello", "world"]')).toBeTruthy(); - // expect(isValidObjectString('() => {}')).toBeTruthy(); - expect(isValidObjectString('hello')).toBeFalsy(); - }); - - it('code2object', () => { - expect(code2object(`{ foo: 12 }`)).toEqual({ foo: 12 }); - expect(code2object(`[]`)).toEqual([]); - expect(code2object(`{this.foo}`)).toBeUndefined(); - expect(code2object(`{foo}`)).toBeUndefined(); - expect(code2object('() => {}')).toEqual('() => {}'); - expect(code2object('hello')).toEqual('hello'); - }); - it('parseDndTrackId', () => { expect(parseDndTrackId('Button:123')).toEqual(['Button', 'Button:123']); expect(parseDndTrackId('Button.Group:123')).toEqual(['Button.Group', 'Button.Group:123']); @@ -104,10 +83,3 @@ describe('string', () => { ); }); }); - -describe('code helper', () => { - it('runCode', () => { - expect(runCode('{}')).toEqual({}); - expect(runCode('{ foo: "foo" }')).toEqual({ foo: 'foo' }); - }); -}); diff --git a/packages/setting-form/src/form-item.tsx b/packages/setting-form/src/form-item.tsx index 2e0f483d..5e0f17f5 100644 --- a/packages/setting-form/src/form-item.tsx +++ b/packages/setting-form/src/form-item.tsx @@ -9,11 +9,10 @@ import { isNil, isString, isWrappedCode, - runCode, wrapCode, } from '@music163/tango-helpers'; import { ErrorBoundary } from '@music163/tango-ui'; -import { value2code } from '@music163/tango-core'; +import { code2value, value2code } from '@music163/tango-core'; import { InputProps } from 'antd'; import { useFormModel, useFormVariable } from './context'; import { FormControl, ToggleCodeButton } from './form-ui'; @@ -89,8 +88,7 @@ function parseFieldValue(fieldValue: any) { const isCodeString = isString(fieldValue) && isWrappedCode(fieldValue); if (isCodeString) { code = getCodeOfWrappedCode(fieldValue); - // FIXME: runCode 的逻辑有问题,拿到的是解析的后的结果,不是真正的代码,这里应该是 code2value - value = runCode(code); + value = code2value(code); } else { code = value2code(fieldValue); value = fieldValue;