-
Notifications
You must be signed in to change notification settings - Fork 432
feat: introduce react-to-dsl plugin #1611
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: ospp-2025/source-to-dsl
Are you sure you want to change the base?
feat: introduce react-to-dsl plugin #1611
Conversation
WalkthroughAdds a new package packages/react-to-dsl that provides a Babel-based transformer (TypeScript) to convert React JSX/TSX into a TinyEngine DSL IAppSchema, with package scaffolding, build/test config, types, utilities, component mappings, tests, and several JSX/CSS testcases. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Caller
participant TR as transformReactToDsl
participant BP as Babel Parser
participant TV as AST Traversal
participant NB as Node Builder
participant CM as Component Mapper
participant SB as Schema Builder
Caller->>TR: transformReactToDsl(code, { filename, css, isBlock })
TR->>BP: parse(code)
BP-->>TR: AST
TR->>TV: traverse(AST)
TV-->>TR: roots, hooks, methods, lifecycles
TR->>NB: buildNodeFromJSX(rootJSX)
NB-->>TR: ISchemaChildrenItem tree
TR->>CM: apply defaultComponentMap & special icon handling
CM-->>TR: normalized nodes (props/styles adjusted)
TR->>SB: assemble IAppSchema (page/block, state, methods, lifeCycles, css, meta)
SB-->>TR: IAppSchema
TR-->>Caller: IAppSchema
note over TR,TV: defensive parsing for loops/expressions and extraction errors
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Pre-merge checks (3 passed)✅ Passed checks (3 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal). Please share your feedback with us on this Discord post. 📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
✨ Finishing touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
🧹 Nitpick comments (27)
packages/react-to-dsl/.gitignore (1)
1-2
: Anchor ignores to package root and add trailing newline.Minor hygiene; also consider ignoring build artifacts here.
Apply:
-output -result +/output +/result +dist/ +coverage/packages/react-to-dsl/tsconfig.json (1)
3-13
: Align TS 5 bundler resolution with explicit ESM semantics.Consider enabling verbatimModuleSyntax to prevent TS from rewriting imports/exports.
"compilerOptions": { "skipLibCheck": true, - "strict": true + "strict": true, + "verbatimModuleSyntax": true },packages/react-to-dsl/src/utils.ts (1)
1-13
: Prefer nanoid for collision-resistance and easier testability.Math.random is fine for demos, but nanoid is standard, faster, and mockable in tests. You already externalize "nanoid" in the build.
-// Local 8-char id generator: digits + lowercase letters only -const _ID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz' -function genId8(): string { - let id = '' - for (let i = 0; i < 8; i++) { - // Use Math.random for simplicity; sufficient for schema ids - const idx = Math.floor(Math.random() * _ID_ALPHABET.length) - id += _ID_ALPHABET.charAt(idx) - } - return id -} +import { customAlphabet } from 'nanoid' +// 36-char alphabet, fixed length 8 +const genId8: () => string = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 8) export { genId8 }If determinism is needed for golden outputs, make the ID generator injectable via transform options and default to genId8.
packages/react-to-dsl/test/testcases/001_normal/input/FormTable.css (2)
19-25
: Prefer flex-start over start for broader Flexbox compatibilityjustify-content: start can behave differently across engines. flex-start is safer for Flexbox containers.
Apply:
- justify-content: start; + justify-content: flex-start;
10-15
: Minor: simplify padding shorthandpadding: 20px 20px can be padding: 20px.
- padding: 20px 20px; + padding: 20px;packages/react-to-dsl/src/constants.ts (1)
1-15
: Consider allowing user-supplied component map overridesExpose an option (e.g., options.componentMap) merged over defaultComponentMap so consumers can customize without forking.
packages/react-to-dsl/README.md (2)
100-129
: Document how to override component mappings and icon rulesAdd a short section showing options.componentMap usage and how special icon mappings can be extended.
20-35
: Call out TypeScript types deliveryREADME promises types; ensure package.json exposes "types" and build emits .d.ts. Add a note or “Build outputs” section so consumers know typings are included.
I can draft the vite + vite-plugin-dts config snippet if helpful.
packages/react-to-dsl/package.json (1)
44-47
: Peer deps likely unnecessaryLibrary parses strings; it doesn’t require react/react-dom at runtime. Consider removing peerDependencies to avoid needless install warnings.
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx (1)
78-86
: Don’t pass children via “children” propUse JSX children for readability and to satisfy linters.
Examples:
- <Typography.Text - children="温馨提示:页面左上角切换区域" - style={{ color: '#8a8e99', fontSize: '12px' }} - ></Typography.Text> + <Typography.Text style={{ color: '#8a8e99', fontSize: '12px' }}> + 温馨提示:页面左上角切换区域 + </Typography.Text> - <Typography.Text children="当前规格" style={{ width: '150px', display: 'inline-block' }} /> + <Typography.Text style={{ width: '150px', display: 'inline-block' }}>当前规格</Typography.Text>Apply similarly to the other instances in this file.
Also applies to: 216-224, 433-452, 220-223
packages/react-to-dsl/src/index.ts (1)
1-2
: Re-export TransformOptions for a complete public API surfaceConsumers need the options type without deep imports. Add a type re-export.
export * from './types' -export { transformReactToDsl } from './transform' +export { transformReactToDsl } from './transform' +export type { TransformOptions } from './transform'packages/react-to-dsl/test/testcases/index.test.js (4)
8-14
: Stabilize testcase discovery orderSort directories to ensure deterministic run order and stable output diffs across platforms.
function getTestCaseDirs(rootDir) { return fs .readdirSync(rootDir, { withFileTypes: true }) .filter((d) => d.isDirectory()) - .map((d) => d.name) + .map((d) => d.name) .filter((name) => name !== 'output' && !name.startsWith('.')) + .sort() }
57-60
: Make the discovery test assert at least one testcase (or skip gracefully)This catches path issues early.
- it(`should discover at least 1 testcase`, () => { - expect(Array.isArray(caseNames)).toBe(true) - // 允许为空,但给出提示;如果后续补充用例即可被自动识别 - }) + it(`should discover at least 1 testcase`, () => { + expect(Array.isArray(caseNames)).toBe(true) + expect(caseNames.length).toBeGreaterThan(0) + })
75-78
: Strengthen schema assertionsAlso assert there is at least one page to guard against empty outputs.
// 基本校验 expect(appSchema).toBeTruthy() expect(Array.isArray(appSchema.pageSchema)).toBe(true) + expect(appSchema.pageSchema.length).toBeGreaterThan(0)
79-81
: Gate filesystem outputs behind an env flag to keep CI cleanAvoid writing artifacts unless explicitly requested.
// 输出到 output/<caseName> - writeOutputs(casesRoot, caseName, appSchema) + if (process.env.WRITE_OUTPUT === '1') { + writeOutputs(casesRoot, caseName, appSchema) + }packages/react-to-dsl/test/basic/basic.test.ts (1)
11-22
: Add a small invariant on fileName for extra safetyThis protects the filename normalization behavior.
const dsl = transformReactToDsl(code, { filename: 'App.tsx' }) expect(dsl.pageSchema.length).toBe(1) const pageOrFolder = dsl.pageSchema[0] if ((pageOrFolder as any).componentName === 'Folder') { throw new Error('unexpected Folder in pageSchema') } const page = pageOrFolder as any + expect(page.fileName).toBe('App') expect(page.children.length).toBe(1) expect(page.children[0].componentName).toBe('div') // props.className should be preserved expect(page.children[0].props.className).toBe('box')
packages/react-to-dsl/test/testcases/002_data-binding/input/DataBindingDemo.jsx (2)
17-17
: Use object style to exercise “style object → CSS string” normalizationA string style bypasses the normalization path. Using an object better validates the feature.
- <div style="padding: 20px;"> + <div style={{ padding: '20px' }}>
12-12
: Remove unused localutils
Not used; trimming avoids noise in fixture processing.
- const utils = {} + // (removed) unusedpackages/react-to-dsl/test/testcases/004_lifecycle/input/lifecycle.jsx (2)
38-38
: Use object style to test style normalization pathSame rationale: drive the object→CSS conversion logic.
- <div style="padding: 20px;"> + <div style={{ padding: '20px' }}>
33-33
: Remove unusedutils
inside renderUnreferenced; safe to drop.
- const utils = {} + // (removed) unusedpackages/react-to-dsl/test/testcases/001_normal/input/FormTable.jsx (2)
188-188
: Redundant conditional renderingThe condition
false ? <TinyIconHelpCircle></TinyIconHelpCircle> : null
is always false and could be simplified.This code can be removed entirely since it never renders:
- {false ? <TinyIconHelpCircle></TinyIconHelpCircle> : null}
98-98
: Unused variable declarationThe
utils
object is declared but never used in the component.- const utils = {}
packages/react-to-dsl/src/transform.ts (3)
36-41
: Consider improving computed property key handlingThe computed property key handling generates code without validation. Consider adding safety checks or documentation about limitations.
const k = prop.computed - ? generate(prop.key).code + ? generate(prop.key).code // Note: Complex computed keys may not be preserved correctly : prop.key.type === 'Identifier' ? prop.key.name : String((prop.key as any).value)
189-195
: Improve CSS conversion robustnessThe
styleObjToCss
function should handle edge cases like numeric values and units.function styleObjToCss(obj: Record<string, any> | string | undefined): any { if (!obj || typeof obj === 'string') return obj const toKebab = (s: string) => s.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase()) return Object.entries(obj) - .map(([k, v]) => `${toKebab(k)}: ${v}`) + .map(([k, v]) => { + // Handle numeric values that need units + const value = typeof v === 'number' && !['opacity', 'zIndex', 'fontWeight'].includes(k) + ? `${v}px` + : v + return `${toKebab(k)}: ${value}` + }) .join('; ') }
344-353
: Consider supporting modern React lifecycle methodsThe lifecycle method list doesn't include newer React lifecycle methods like
getDerivedStateFromProps
andgetSnapshotBeforeUpdate
.const lifecycleNames = new Set([ 'constructor', 'componentDidMount', 'componentWillUnmount', 'componentDidUpdate', 'componentDidCatch', 'shouldComponentUpdate', 'getSnapshotBeforeUpdate', - 'componentWillReceiveProps' + 'componentWillReceiveProps', + 'getDerivedStateFromProps', + 'getDerivedStateFromError' ])packages/react-to-dsl/src/types.ts (2)
71-71
: Consider using a more specific type for contentThe
content
field inIUtilsItem
is typed asobject
, which provides minimal type safety. Consider using a more specific type or union type.export interface IUtilsItem { name: string type: 'npm' | 'function' - content: object + content: Record<string, any> | { package: string; version: string } | IFuncType }
49-49
: Nested Record type may be confusingThe
lifeCycles
type uses nested Records which might be confusing. Consider simplifying or adding documentation.lifeCycles: { - [key: string]: Record<string, IFuncType> + [lifecycleName: string]: Record<string, IFuncType> // lifecycle name -> method implementations }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (18)
packages/react-to-dsl/.gitignore
(1 hunks)packages/react-to-dsl/README.md
(1 hunks)packages/react-to-dsl/package.json
(1 hunks)packages/react-to-dsl/src/constants.ts
(1 hunks)packages/react-to-dsl/src/index.ts
(1 hunks)packages/react-to-dsl/src/transform.ts
(1 hunks)packages/react-to-dsl/src/types.ts
(1 hunks)packages/react-to-dsl/src/utils.ts
(1 hunks)packages/react-to-dsl/test/basic/basic.test.ts
(1 hunks)packages/react-to-dsl/test/testcases/001_normal/input/FormTable.css
(1 hunks)packages/react-to-dsl/test/testcases/001_normal/input/FormTable.jsx
(1 hunks)packages/react-to-dsl/test/testcases/002_data-binding/input/DataBindingDemo.jsx
(1 hunks)packages/react-to-dsl/test/testcases/003_createVM/input/createVm.css
(1 hunks)packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx
(1 hunks)packages/react-to-dsl/test/testcases/004_lifecycle/input/lifecycle.jsx
(1 hunks)packages/react-to-dsl/test/testcases/index.test.js
(1 hunks)packages/react-to-dsl/tsconfig.json
(1 hunks)packages/react-to-dsl/vite.config.ts
(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-01-14T08:42:18.574Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1038
File: packages/plugins/block/index.js:24-24
Timestamp: 2025-01-14T08:42:18.574Z
Learning: In the tiny-engine project, breaking changes are documented in the changelog rather than in JSDoc comments or separate migration guides.
Applied to files:
packages/react-to-dsl/README.md
📚 Learning: 2024-10-09T01:47:35.507Z
Learnt from: chilingling
PR: opentiny/tiny-engine#583
File: packages/build/vite-config/index.js:1-1
Timestamp: 2024-10-09T01:47:35.507Z
Learning: The `getDefaultConfig` function inside `packages/build/vite-config/src/default-config.js` is intended to remain and be called internally. Ensure no references to this function exist outside of this file.
Applied to files:
packages/react-to-dsl/vite.config.ts
📚 Learning: 2024-09-30T07:51:10.036Z
Learnt from: chilingling
PR: opentiny/tiny-engine#837
File: packages/vue-generator/src/plugins/genDependenciesPlugin.js:66-66
Timestamp: 2024-09-30T07:51:10.036Z
Learning: In the `tiny-engine` project, `opentiny/tiny-engine-dsl-vue` refers to the current package itself, and importing types from it may cause circular dependencies.
Applied to files:
packages/react-to-dsl/package.json
🧬 Code graph analysis (4)
packages/react-to-dsl/src/transform.ts (3)
packages/react-to-dsl/src/types.ts (3)
ISchemaChildrenItem
(26-33)IAppSchema
(93-105)IPageSchema
(44-66)packages/react-to-dsl/src/utils.ts (1)
genId8
(13-13)packages/react-to-dsl/src/constants.ts (1)
defaultComponentMap
(1-15)
packages/react-to-dsl/test/testcases/002_data-binding/input/DataBindingDemo.jsx (1)
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx (2)
state
(7-24)utils
(26-26)
packages/react-to-dsl/test/testcases/index.test.js (1)
packages/react-to-dsl/src/transform.ts (1)
transformReactToDsl
(223-406)
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx (1)
packages/react-to-dsl/test/testcases/002_data-binding/input/DataBindingDemo.jsx (2)
state
(6-10)utils
(12-12)
🪛 Biome (2.1.2)
packages/react-to-dsl/test/testcases/001_normal/input/FormTable.jsx
[error] 107-107: This variable is used before its declaration.
The variable is declared here:
(lint/correctness/noInvalidUseBeforeDeclaration)
[error] 198-198: Unexpected constant condition.
(lint/correctness/noConstantCondition)
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx
[error] 80-80: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 84-84: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 136-137: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 148-148: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 162-162: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 228-229: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 235-235: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 360-360: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
[error] 436-436: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 446-446: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 449-450: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 452-453: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 460-460: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 464-464: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 471-471: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
🔇 Additional comments (13)
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.css (1)
1-4
: LGTM for test fixture CSS.Clear, minimal, and appropriate for the case input.
packages/react-to-dsl/src/constants.ts (1)
1-15
: Verify several component mappings (possible mismatches)
- Steps -> TinyTimeLine: confirm this is intended (Steps vs Timeline semantics).
- Radio.Group -> TinyButtonGroup: verify; many ecosystems use RadioGroup.
- Typography.Text -> Text: ensure the target is correct in Tiny DSL.
I can adjust the map once you confirm the desired targets.
packages/react-to-dsl/package.json (1)
29-43
: Align Babel package versions to avoid AST incompatibilitiesparser/traverse/generator are ~7.23.2 while core/types are 7.28.x. Keep all Babel 7 packages on the same minor for safety.
I proposed pinning core/types to ^7.23.2, but aligning all to a single modern 7.x is fine—pick one and standardize.
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx (3)
245-251
: Form props look non-antd (likely Tiny-style)labelCol="80px", layout={false}, label-position, label-width aren’t antd props. If this file is only a transform fixture, fine; otherwise these won’t function in real antd.
Also applies to: 307-313
184-214
: Table props appear non-antdeditConfig, options, auto-resize aren’t antd Table props. Confirm intent (fixture vs runnable example).
351-352
: Icon usage matches special-case mapping—goodDatabaseOutlined and PlusOutlined presence should exercise your icon mapping logic.
Also applies to: 389-389
packages/react-to-dsl/test/testcases/002_data-binding/input/DataBindingDemo.jsx (1)
18-33
: Confirm alias mapping for Antd componentsYou’re using aliased tags (AntdInput/AntdCheckbox). Verify the component-mapping logic handles aliases; otherwise these won’t remap to TinyInput/TinyCheckbox.
Would you like me to add a focused test to assert the mapped componentName values for aliased imports?
packages/react-to-dsl/test/testcases/004_lifecycle/input/lifecycle.jsx (1)
11-29
: Verify lifecycle and class-field extraction into schemaEnsure these methods land in page.lifeCycles and page.methods respectively:
- componentDidMount/WillUnmount/DidUpdate/DidCatch → lifeCycles
- handleClick (class field arrow) → methods
If helpful, I can add assertions in the integration test to check for these keys in the emitted schema.
packages/react-to-dsl/src/transform.ts (4)
15-65
: Good: Comprehensive AST expression handlingThe
astExprToValue
function handles various AST node types effectively, with proper fallback to JSExpression for complex expressions.
137-158
: Well-implemented map pattern recognitionThe code effectively recognizes and converts common
arr.map()
patterns to loop expressions in the DSL. This is a key feature for React-to-DSL transformation.
302-304
: Good error handling strategySilently catching errors during state extraction prevents partial failures from breaking the entire transformation. This resilient approach is appropriate for a transformer tool.
388-392
: Error handling for component mappingGood practice to wrap the component mapping in a try-catch block to prevent mapping errors from breaking the transformation.
packages/react-to-dsl/src/types.ts (1)
1-105
: Well-structured type definitionsThe type definitions provide a comprehensive and well-organized schema for the DSL structure. The interfaces are properly typed with clear naming conventions.
{ | ||
"name": "@opentiny/tiny-engine-react-to-dsl", | ||
"version": "0.1.0", | ||
"description": "Transform React source code (JSX/TSX) to TinyEngine DSL schema.", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"type": "module", | ||
"main": "dist/index.js", | ||
"module": "dist/index.js", | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"build": "vite build", | ||
"test": "vitest run" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/opentiny/tiny-engine", | ||
"directory": "packages/react-to-dsl" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/opentiny/tiny-engine/issues" | ||
}, | ||
"author": "OpenTiny Team", | ||
"license": "MIT", | ||
"homepage": "https://opentiny.design/tiny-engine", | ||
"dependencies": { | ||
"@babel/generator": "~7.23.2", | ||
"@babel/parser": "~7.23.2", | ||
"@babel/traverse": "~7.23.2", | ||
"nanoid": "^5.0.7" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "^7.28.2", | ||
"@babel/types": "^7.28.2", | ||
"@types/babel__generator": "^7.27.0", | ||
"@types/babel__traverse": "^7.28.0", | ||
"typescript": "~5.4.5", | ||
"vite": "^5.4.2", | ||
"vitest": "^1.6.0" | ||
}, | ||
"peerDependencies": { | ||
"react": ">=17", | ||
"react-dom": ">=17" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Publish typings and modern exports
Type consumers won’t see your .d.ts without a types field/exports. Also consider shipping README/LICENSE.
Apply:
{
"name": "@opentiny/tiny-engine-react-to-dsl",
"version": "0.1.0",
"description": "Transform React source code (JSX/TSX) to TinyEngine DSL schema.",
"publishConfig": {
"access": "public"
},
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js",
+ "default": "./dist/index.js"
+ }
+ },
"files": [
- "dist"
+ "dist",
+ "README.md",
+ "LICENSE"
],
"scripts": {
"build": "vite build",
"test": "vitest run"
},
@@
"devDependencies": {
- "@babel/core": "^7.28.2",
- "@babel/types": "^7.28.2",
+ "@babel/core": "^7.23.2",
+ "@babel/types": "^7.23.2",
"@types/babel__generator": "^7.27.0",
"@types/babel__traverse": "^7.28.0",
+ "vite-plugin-dts": "^3.6.0",
"typescript": "~5.4.5",
"vite": "^5.4.2",
"vitest": "^1.6.0"
},
+ "engines": {
+ "node": ">=18"
+ }
}
Note: use vite-plugin-dts in vite.config.ts to emit declarations.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
{ | |
"name": "@opentiny/tiny-engine-react-to-dsl", | |
"version": "0.1.0", | |
"description": "Transform React source code (JSX/TSX) to TinyEngine DSL schema.", | |
"publishConfig": { | |
"access": "public" | |
}, | |
"type": "module", | |
"main": "dist/index.js", | |
"module": "dist/index.js", | |
"files": [ | |
"dist" | |
], | |
"scripts": { | |
"build": "vite build", | |
"test": "vitest run" | |
}, | |
"repository": { | |
"type": "git", | |
"url": "https://github.com/opentiny/tiny-engine", | |
"directory": "packages/react-to-dsl" | |
}, | |
"bugs": { | |
"url": "https://github.com/opentiny/tiny-engine/issues" | |
}, | |
"author": "OpenTiny Team", | |
"license": "MIT", | |
"homepage": "https://opentiny.design/tiny-engine", | |
"dependencies": { | |
"@babel/generator": "~7.23.2", | |
"@babel/parser": "~7.23.2", | |
"@babel/traverse": "~7.23.2", | |
"nanoid": "^5.0.7" | |
}, | |
"devDependencies": { | |
"@babel/core": "^7.28.2", | |
"@babel/types": "^7.28.2", | |
"@types/babel__generator": "^7.27.0", | |
"@types/babel__traverse": "^7.28.0", | |
"typescript": "~5.4.5", | |
"vite": "^5.4.2", | |
"vitest": "^1.6.0" | |
}, | |
"peerDependencies": { | |
"react": ">=17", | |
"react-dom": ">=17" | |
} | |
} | |
{ | |
"name": "@opentiny/tiny-engine-react-to-dsl", | |
"version": "0.1.0", | |
"description": "Transform React source code (JSX/TSX) to TinyEngine DSL schema.", | |
"publishConfig": { | |
"access": "public" | |
}, | |
"type": "module", | |
"main": "dist/index.js", | |
"module": "dist/index.js", | |
"types": "dist/index.d.ts", | |
"exports": { | |
".": { | |
"types": "./dist/index.d.ts", | |
"import": "./dist/index.js", | |
"default": "./dist/index.js" | |
} | |
}, | |
"files": [ | |
"dist", | |
"README.md", | |
"LICENSE" | |
], | |
"scripts": { | |
"build": "vite build", | |
"test": "vitest run" | |
}, | |
"repository": { | |
"type": "git", | |
"url": "https://github.com/opentiny/tiny-engine", | |
"directory": "packages/react-to-dsl" | |
}, | |
"bugs": { | |
"url": "https://github.com/opentiny/tiny-engine/issues" | |
}, | |
"author": "OpenTiny Team", | |
"license": "MIT", | |
"homepage": "https://opentiny.design/tiny-engine", | |
"dependencies": { | |
"@babel/generator": "~7.23.2", | |
"@babel/parser": "~7.23.2", | |
"@babel/traverse": "~7.23.2", | |
"nanoid": "^5.0.7" | |
}, | |
"devDependencies": { | |
"@babel/core": "^7.23.2", | |
"@babel/types": "^7.23.2", | |
"@types/babel__generator": "^7.27.0", | |
"@types/babel__traverse": "^7.28.0", | |
"vite-plugin-dts": "^3.6.0", | |
"typescript": "~5.4.5", | |
"vite": "^5.4.2", | |
"vitest": "^1.6.0" | |
}, | |
"peerDependencies": { | |
"react": ">=17", | |
"react-dom": ">=17" | |
}, | |
"engines": { | |
"node": ">=18" | |
} | |
} |
🤖 Prompt for AI Agents
In packages/react-to-dsl/package.json lines 1-48, the package is missing a
"types" entry and proper "exports" entries so consumers won't get emitted .d.ts
files, and README/LICENSE are not shipped; update package.json to add a "types"
field pointing to the emitted declaration file (e.g. dist/index.d.ts) and add an
"exports" map that exposes the ESM/CJS entry and the types entry for the package
root, add README.md and LICENSE files to the package root and ensure they are
included in the published files (or remove "files" restriction), and update
vite.config.ts to use vite-plugin-dts to emit declarations during build (enable
declaration output to dist). Ensure package.json version/peerDependencies remain
correct and run the build to verify dist/*.d.ts exists before publishing.
|
||
const FormTable = (props = {}) => { | ||
const [state, setState] = React.useState({ | ||
IconPlusSquare: this.utils.IconPlusSquare(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Invalid this.utils
reference in functional component
The code references this.utils.IconPlusSquare()
within a functional component's state initialization. Functional components don't have a this
context. This will cause a runtime error.
- IconPlusSquare: this.utils.IconPlusSquare(),
+ IconPlusSquare: null, // TODO: Initialize appropriately
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
IconPlusSquare: this.utils.IconPlusSquare(), | |
IconPlusSquare: null, // TODO: Initialize appropriately |
🤖 Prompt for AI Agents
In packages/react-to-dsl/test/testcases/001_normal/input/FormTable.jsx around
line 10, the state init incorrectly calls this.utils.IconPlusSquare() inside a
functional component (no this); replace the reference with a valid value by
either importing the utils module and calling utils.IconPlusSquare(), using a
locally defined utils variable, or accessing it from props (e.g.,
props.utils.IconPlusSquare()) so the call does not rely on this.
filter: { | ||
layout: 'input,enum,default,extends,base', | ||
inputFilter: { | ||
component: this.utils.Numeric, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: this.utils.Numeric
reference in functional component
The code attempts to access this.utils.Numeric
inside a functional component, which will fail at runtime.
- component: this.utils.Numeric,
+ component: null, // TODO: Import and use Numeric component properly
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
component: this.utils.Numeric, | |
// before (invalid in a functional component): | |
component: null, // TODO: Import and use Numeric component properly |
🤖 Prompt for AI Agents
In packages/react-to-dsl/test/testcases/001_normal/input/FormTable.jsx around
line 41, the code uses this.utils.Numeric inside a functional component which
will throw because functional components have no this; replace the reference by
either importing Numeric directly from the utils module or by accessing it via
props/context (e.g., props.utils.Numeric or the appropriate context hook) and
update the component to use that imported or passed-in Numeric component instead
of this.utils.Numeric.
{ id: '7', name: 'KBG物业有限公司', city: '赤壁', employees: 400, boole: false }, | ||
{ id: '8', name: '深圳市福德宝网络技术有限公司', boole: true, city: '厦门', employees: 540 } | ||
], | ||
status: this.statusData, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Invalid this.statusData
reference
The code assigns this.statusData
to the state object, but statusData
is defined as a local function, not a class method.
- status: this.statusData,
+ status: statusData(),
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
status: this.statusData, | |
status: statusData(), |
🤖 Prompt for AI Agents
In packages/react-to-dsl/test/testcases/001_normal/input/FormTable.jsx around
line 90, the code assigns this.statusData to state but statusData is a locally
defined function (not a class instance method); fix by either calling the local
function (status: statusData()) to assign its return value, or move/convert the
function into a class method and reference it as this.statusData()
consistently—update the code to one of these two approaches so the reference is
valid.
buttons: [ | ||
{ type: 'primary', text: '主要操作' }, | ||
{ type: 'success', text: '成功操作' }, | ||
{ type: 'danger', text: t('operation.danger') } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: t
function used before declaration
The t
function from useTranslation
is referenced in the initial state before it's declared on line 100.
Move the useTranslation
hook call before the state initialization:
const FormTable = (props = {}) => {
+ const { t } = useTranslation()
const [state, setState] = React.useState({
// ... state initialization
- { type: 'danger', text: t('operation.danger') }
+ { type: 'danger', text: t('operation.danger') }
]
})
const utils = {}
- const { t } = useTranslation()
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
{ type: 'danger', text: t('operation.danger') } | |
const FormTable = (props = {}) => { | |
// Move hook above state so `t` is available in the initializer | |
const { t } = useTranslation() | |
const [state, setState] = React.useState({ | |
// ... state initialization | |
{ type: 'danger', text: t('operation.danger') } | |
] | |
}) | |
const utils = {} | |
// (Removed the duplicate `const { t } = useTranslation()` here) | |
// ...rest of component... | |
} |
🤖 Prompt for AI Agents
In packages/react-to-dsl/test/testcases/001_normal/input/FormTable.jsx around
line 94, the t function from useTranslation is being referenced in the
component's initial state before useTranslation is called; move the
useTranslation() hook call to precede the state initialization so t is defined
when building the initial state (i.e., call const { t } = useTranslation() above
where state/defaults are declared), then keep the rest of the state and usage
unchanged.
{state.dataDisk.map((item) => ( | ||
<div style={{ marginTop: '12px', display: 'flex' }}> | ||
<DatabaseOutlined style={{ marginRight: '10px', width: '16px', height: '16px' }}></DatabaseOutlined> | ||
<Select | ||
value={state.formData.diskType} | ||
onChange={(e) => setState((prev) => ({ ...prev, formData: { ...prev.formData, diskType: e } }))} | ||
placeholder="请选择" | ||
options={[ | ||
{ value: '1', label: '黄金糕' }, | ||
{ value: '2', label: '双皮奶' } | ||
]} | ||
style={{ width: '200px', marginRight: '10px' }} | ||
></Select> | ||
<Input | ||
placeholder="请输入" | ||
value={state.inputValues.dataDiskSize} | ||
onChange={(e) => | ||
setState((prev) => ({ | ||
...prev, | ||
inputValues: { ...prev.inputValues, dataDiskSize: e.target.value } | ||
})) | ||
} | ||
style={{ width: '120px', marginRight: '10px' }} | ||
></Input> | ||
<Typography.Text | ||
label="GiB | ||
IOPS上限600,IOPS突发上限5,000" | ||
style={{ color: '#575d6c', fontSize: '12px', marginRight: '10px' }} | ||
></Typography.Text> | ||
<Input | ||
placeholder="请输入" | ||
value={state.inputValues.diskLabel} | ||
onChange={(e) => | ||
setState((prev) => ({ ...prev, inputValues: { ...prev.inputValues, diskLabel: e.target.value } })) | ||
} | ||
style={{ width: '120px' }} | ||
></Input> | ||
</div> | ||
))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add keys to elements rendered in a list
Missing key on the mapped
- {state.dataDisk.map((item) => (
- <div style={{ marginTop: '12px', display: 'flex' }}>
+ {state.dataDisk.map((item, idx) => (
+ <div key={item ?? idx} style={{ marginTop: '12px', display: 'flex' }}>
...
</div>
))}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
{state.dataDisk.map((item) => ( | |
<div style={{ marginTop: '12px', display: 'flex' }}> | |
<DatabaseOutlined style={{ marginRight: '10px', width: '16px', height: '16px' }}></DatabaseOutlined> | |
<Select | |
value={state.formData.diskType} | |
onChange={(e) => setState((prev) => ({ ...prev, formData: { ...prev.formData, diskType: e } }))} | |
placeholder="请选择" | |
options={[ | |
{ value: '1', label: '黄金糕' }, | |
{ value: '2', label: '双皮奶' } | |
]} | |
style={{ width: '200px', marginRight: '10px' }} | |
></Select> | |
<Input | |
placeholder="请输入" | |
value={state.inputValues.dataDiskSize} | |
onChange={(e) => | |
setState((prev) => ({ | |
...prev, | |
inputValues: { ...prev.inputValues, dataDiskSize: e.target.value } | |
})) | |
} | |
style={{ width: '120px', marginRight: '10px' }} | |
></Input> | |
<Typography.Text | |
label="GiB | |
IOPS上限600,IOPS突发上限5,000" | |
style={{ color: '#575d6c', fontSize: '12px', marginRight: '10px' }} | |
></Typography.Text> | |
<Input | |
placeholder="请输入" | |
value={state.inputValues.diskLabel} | |
onChange={(e) => | |
setState((prev) => ({ ...prev, inputValues: { ...prev.inputValues, diskLabel: e.target.value } })) | |
} | |
style={{ width: '120px' }} | |
></Input> | |
</div> | |
))} | |
{state.dataDisk.map((item, idx) => ( | |
<div key={item ?? idx} style={{ marginTop: '12px', display: 'flex' }}> | |
<DatabaseOutlined style={{ marginRight: '10px', width: '16px', height: '16px' }}></DatabaseOutlined> | |
<Select | |
value={state.formData.diskType} | |
onChange={(e) => setState((prev) => ({ ...prev, formData: { ...prev.formData, diskType: e } }))} | |
placeholder="请选择" | |
options={[ | |
{ value: '1', label: '黄金糕' }, | |
{ value: '2', label: '双皮奶' } | |
]} | |
style={{ width: '200px', marginRight: '10px' }} | |
></Select> | |
<Input | |
placeholder="请输入" | |
value={state.inputValues.dataDiskSize} | |
onChange={(e) => | |
setState((prev) => ({ | |
...prev, | |
inputValues: { ...prev.inputValues, dataDiskSize: e.target.value } | |
})) | |
} | |
style={{ width: '120px', marginRight: '10px' }} | |
></Input> | |
<Typography.Text | |
label="GiB | |
IOPS上限600,IOPS突发上限5,000" | |
style={{ color: '#575d6c', fontSize: '12px', marginRight: '10px' }} | |
></Typography.Text> | |
<Input | |
placeholder="请输入" | |
value={state.inputValues.diskLabel} | |
onChange={(e) => | |
setState((prev) => ({ ...prev, inputValues: { ...prev.inputValues, diskLabel: e.target.value } })) | |
} | |
style={{ width: '120px' }} | |
></Input> | |
</div> | |
))} |
🧰 Tools
🪛 Biome (2.1.2)
[error] 360-360: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
🤖 Prompt for AI Agents
In packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx around
lines 349 to 387, the mapped <div> inside state.dataDisk.map is missing a React
key; add a key prop to the top-level element in the map (e.g., key={item.id} if
dataDisk items have a unique id, otherwise key={index} as a last resort) to
remove React warnings and ensure stable identity when rendering the list.
"compilerOptions": { | ||
"composite": false, | ||
"declaration": false, | ||
"emitDeclarationOnly": false, | ||
"module": "ESNext", | ||
"moduleResolution": "bundler", | ||
"target": "ES2020", | ||
"lib": ["ES2020"], | ||
"skipLibCheck": true, | ||
"strict": true | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Ensure consumers get TypeScript types.
Right now declaration emit is disabled. Either add vite-plugin-dts (preferred; see vite.config.ts comment) or enable declaration emit to publish .d.ts with the package.
If you prefer tsc-based emit, update:
"compilerOptions": {
- "declaration": false,
- "emitDeclarationOnly": false,
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "declarationDir": "./dist/types",
"module": "ESNext",
And set "types" in package.json to "dist/types/index.d.ts".
🏁 Script executed:
#!/bin/bash
# List contents of the react-to-dsl package
echo "=== packages/react-to-dsl/ ==="
ls -R packages/react-to-dsl
# Display tsconfig.json to confirm declaration settings
echo
echo "=== packages/react-to-dsl/tsconfig.json ==="
sed -n '1,200p' packages/react-to-dsl/tsconfig.json
# Locate and inspect Vite config for vite-plugin-dts usage
echo
echo "=== Finding vite.config.ts ==="
find packages/react-to-dsl -type f -name 'vite.config.ts'
if [ -f packages/react-to-dsl/vite.config.ts ]; then
echo
echo "=== packages/react-to-dsl/vite.config.ts ==="
sed -n '1,200p' packages/react-to-dsl/vite.config.ts
fi
# Check package.json for a "types" entry
echo
echo "=== packages/react-to-dsl/package.json ==="
sed -n '1,200p' packages/react-to-dsl/package.json
Length of output: 3710
🏁 Script executed:
#!/bin/bash
# Display root tsconfig.json to check for global declaration settings
echo "=== Root tsconfig.json ==="
sed -n '1,200p' tsconfig.json
echo
echo "=== Declaration flags in root tsconfig.json ==="
grep -R '"declaration"' -n tsconfig.json || echo "no declaration flags found"
Length of output: 418
Enable declaration output for TypeScript consumers
Your package currently doesn’t emit any .d.ts files, so downstream TS projects won’t receive type definitions. Configure either vite-plugin-dts or TSC-based emit:
In packages/react-to-dsl/tsconfig.json update:
"compilerOptions": {
- "declaration": false,
- "emitDeclarationOnly": false,
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "declarationDir": "./dist/types",
"module": "ESNext",
And in packages/react-to-dsl/package.json add:
"types": "dist/types/index.d.ts",
🤖 Prompt for AI Agents
In packages/react-to-dsl/tsconfig.json (lines 3-13) the project currently
disables declaration output; enable TypeScript declaration generation by setting
"declaration": true and adding a "declarationDir": "dist/types" (and optionally
"declarationMap": true) so .d.ts files are emitted to dist/types; either run a
separate tsc pass with --emitDeclarationOnly during your build or configure
vite-plugin-dts to emit declarations into that folder. Also update
packages/react-to-dsl/package.json to include "types": "dist/types/index.d.ts"
so downstream consumers pick up the bundled types.
@@ -0,0 +1,23 @@ | ||
import { defineConfig } from 'vite' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add DTS generation to ship types.
Generate .d.ts via vite-plugin-dts so consumers get the declared API.
import { defineConfig } from 'vite'
+import dts from 'vite-plugin-dts'
import path from 'node:path'
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import { defineConfig } from 'vite' | |
import { defineConfig } from 'vite' | |
import dts from 'vite-plugin-dts' | |
import path from 'node:path' |
🤖 Prompt for AI Agents
In packages/react-to-dsl/vite.config.ts around line 1, the project doesn’t
generate .d.ts files for consumers; install and import vite-plugin-dts and add
it to the Vite plugins list (e.g., import dts from 'vite-plugin-dts' and include
dts({ insertTypesEntry: true }) in defineConfig plugins), ensure your build
config exposes the library entry (build.lib.entry) so the plugin can emit
declarations into dist (or configure outputDir), and confirm tsconfig has
declaration enabled or let the plugin generate declarations — this will produce
.d.ts files alongside the build for consumers.
export default defineConfig({ | ||
publicDir: false, | ||
resolve: {}, | ||
base: './', | ||
define: { | ||
'import.meta': 'import.meta' | ||
}, | ||
build: { | ||
sourcemap: true, | ||
lib: { | ||
entry: path.resolve(__dirname, './src/index.ts'), | ||
name: 'reactToDsl', | ||
fileName: () => 'index.js', | ||
formats: ['es'] | ||
}, | ||
rollupOptions: { | ||
external: [/^@babel.*/, 'react', 'react-dom', 'nanoid'] | ||
} | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Wire up vite-plugin-dts and drop the unnecessary import.meta define.
The manual import.meta define is typically unnecessary for lib mode; types emission is the missing piece.
export default defineConfig({
publicDir: false,
resolve: {},
base: './',
- define: {
- 'import.meta': 'import.meta'
- },
+ plugins: [dts({ insertTypesEntry: true, outDir: 'dist' })],
build: {
sourcemap: true,
lib: {
entry: path.resolve(__dirname, './src/index.ts'),
name: 'reactToDsl',
fileName: () => 'index.js',
formats: ['es']
},
rollupOptions: {
external: [/^@babel.*/, 'react', 'react-dom', 'nanoid']
}
}
})
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/react-to-dsl/vite.config.ts around lines 4 to 23, remove the manual
define entry for 'import.meta' and wire up vite-plugin-dts: import the plugin at
the top, add a plugins array to defineConfig that includes dts() (configure
insertTypesEntry: true or emitDeclarationOnly as needed for your package), and
keep the existing build.lib and rollupOptions; this will enable type declaration
emission for the library and remove the unnecessary import.meta define.
rollupOptions: { | ||
external: [/^@babel.*/, 'react', 'react-dom', 'nanoid'] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify externals are declared as deps/peers.
Externalizing @babel/*, react, react-dom, and nanoid requires they be present in dependencies or peerDependencies of packages/react-to-dsl/package.json.
Run:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
import json, pathlib
p = pathlib.Path('packages/react-to-dsl/package.json')
d = json.loads(p.read_text())
deps = set((d.get('dependencies') or {}).keys()) | set((d.get('peerDependencies') or {}).keys())
need = {'@babel/parser','@babel/traverse','@babel/generator','@babel/types','react','react-dom','nanoid'}
print('types field:', d.get('types'))
print('declared deps/peers:', sorted(deps))
print('missing externals (declare as deps/peers):', sorted(need - deps))
PY
Length of output: 218
Declare missing external @babel/types in package.json
The rollup externals include @babel/types
, but it’s not listed under dependencies or peerDependencies in packages/react-to-dsl/package.json—add it to prevent unresolved-module errors.
🤖 Prompt for AI Agents
In packages/react-to-dsl/vite.config.ts around lines 19–21 the rollup externals
reference @babel/types but packages/react-to-dsl/package.json does not declare
it; add "@babel/types" to that package.json (either dependencies or
peerDependencies as appropriate for how the package is consumed), run your
package manager to update the lockfile/hoisting, and publish or link the change
so Rollup/Vite no longer throws unresolved-module errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx (2)
286-287
: Typography.Text: remove unsupported “label” prop; use children instead (duplicate of earlier review).AntD Typography.Text doesn’t have a label prop, so text won’t render. Inline the text as children.
- <Typography.Text label="请注意操作系统的语言类型。" style={{ color: '#e37d29' }}></Typography.Text> + <Typography.Text style={{ color: '#e37d29' }}>请注意操作系统的语言类型。</Typography.Text>- <Typography.Text - label="GiB -IOPS上限240,IOPS突发上限5,000" - style={{ color: '#575d6c', fontSize: '12px' }} - ></Typography.Text> + <Typography.Text style={{ color: '#575d6c', fontSize: '12px' }}> + GiB <br /> + IOPS上限240,IOPS突发上限5,000 + </Typography.Text>- <Typography.Text - label="GiB -IOPS上限600,IOPS突发上限5,000" - style={{ color: '#575d6c', fontSize: '12px', marginRight: '10px' }} - ></Typography.Text> + <Typography.Text style={{ color: '#575d6c', fontSize: '12px', marginRight: '10px' }}> + GiB <br /> + IOPS上限600,IOPS突发上限5,000 + </Typography.Text>- <div style={{ display: 'flex', marginTop: '12px', borderRadius: '0px' }}> - <Typography.Text - label="增加一块数据盘" - style={{ fontSize: '12px', borderRadius: '0px', marginRight: '10px' }} - ></Typography.Text> - <Typography.Text - label="您还可以挂载 21 块磁盘(云硬盘)" - style={{ color: '#8a8e99', fontSize: '12px' }} - ></Typography.Text> - </div> + <div style={{ display: 'flex', marginTop: '12px', borderRadius: '0px' }}> + <Typography.Text style={{ fontSize: '12px', borderRadius: '0px', marginRight: '10px' }}> + 增加一块数据盘 + </Typography.Text> + <Typography.Text style={{ color: '#8a8e99', fontSize: '12px' }}> + 您还可以挂载 21 块磁盘(云硬盘) + </Typography.Text> + </div>Also applies to: 333-337, 374-377, 389-397
349-387
: Add React keys to list items (duplicate of earlier review).Missing key on the mapped div will trigger React warnings.
- {state.dataDisk.map((item) => ( - <div style={{ marginTop: '12px', display: 'flex' }}> + {state.dataDisk.map((item, idx) => ( + <div key={item ?? idx} style={{ marginTop: '12px', display: 'flex' }}>
🧹 Nitpick comments (6)
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx (4)
78-86
: Avoid using the “children” prop; place text as JSX children.Biome flags these; prefer <Typography.Text>内容</Typography.Text>. Apply pattern across the file.
- <Typography.Text - children="温馨提示:页面左上角切换区域" - style={{ color: '#8a8e99', fontSize: '12px' }} - ></Typography.Text> + <Typography.Text style={{ color: '#8a8e99', fontSize: '12px' }}> + 温馨提示:页面左上角切换区域 + </Typography.Text> - <Typography.Text children="vCPUs" style={{ width: '80px' }}></Typography.Text> + <Typography.Text style={{ width: '80px' }}>vCPUs</Typography.Text> - <Typography.Text children="规格名称" style={{ width: '120px' }}></Typography.Text> + <Typography.Text style={{ width: '120px' }}>规格名称</Typography.Text> - <Typography.Text - children="当前规格" - style={{ width: '150px', display: 'inline-block' }} - ></Typography.Text> + <Typography.Text style={{ width: '150px', display: 'inline-block' }}> + 当前规格 + </Typography.Text> - <Typography.Text children="配置费用" style={{ fontSize: '12px' }}></Typography.Text> + <Typography.Text style={{ fontSize: '12px' }}>配置费用</Typography.Text> - <Typography.Text children="/小时" style={{ fontSize: '12px' }}></Typography.Text> + <Typography.Text style={{ fontSize: '12px' }}>/小时</Typography.Text>Also applies to: 132-146, 156-166, 216-224, 431-451
245-249
: Form props don’t match AntD API; verify intent.Props like layout={false}, labelCol="80px", label-position, label-width aren’t valid in antd Form. If this is purely a parser fixture, okay; otherwise replace with supported props (e.g., layout="horizontal", labelCol={{ span: 6 }}) or drop unsupported ones.
Also applies to: 306-312, 341-347
26-26
: Remove unused variable.utils is declared but never used.
- const utils = {}
419-457
: Col span should be numbers, not strings.Use span={16}, span={6}, etc., to match AntD prop types and avoid warnings.
- <Col span="16"> + <Col span={16}> ... - <Col span="6"> + <Col span={6}> ... - <Col span="7"> + <Col span={7}> ... - <Col - span="8" + <Col + span={8}packages/react-to-dsl/test/testcases/index.test.js (2)
8-14
: Make discovery deterministic by sorting directories and files.Stabilizes CI and output.
function getTestCaseDirs(rootDir) { return fs .readdirSync(rootDir, { withFileTypes: true }) .filter((d) => d.isDirectory()) .map((d) => d.name) - .filter((name) => name !== 'output' && !name.startsWith('.')) + .filter((name) => name !== 'output' && !name.startsWith('.')) + .sort() } function findInputSource(caseDir) { const inputDir = path.join(caseDir, 'input') if (!fs.existsSync(inputDir)) return null - const files = fs.readdirSync(inputDir) + const files = fs.readdirSync(inputDir).sort() const src = files.find((f) => /\.(jsx|tsx)$/.test(f)) return src ? path.join(inputDir, src) : null } function readAllCss(caseDir) { const inputDir = path.join(caseDir, 'input') if (!fs.existsSync(inputDir)) return '' - const files = fs.readdirSync(inputDir) - const cssFiles = files.filter((f) => f.endsWith('.css')) + const files = fs.readdirSync(inputDir).sort() + const cssFiles = files.filter((f) => f.endsWith('.css')) const contents = cssFiles.map((f) => fs.readFileSync(path.join(inputDir, f), 'utf-8')) return contents.join('\n') }Also applies to: 36-42, 45-52
176-178
: Gate filesystem outputs behind an env flag to keep CI/worktrees clean.Prevents test runs from modifying the repo unless explicitly enabled.
- // 输出到 output/<caseName> - writeOutputs(casesRoot, caseName, appSchema) + // 输出到 output/<caseName>(仅在显式开启时) + if (process.env.WRITE_DSL_OUTPUT === '1') { + writeOutputs(casesRoot, caseName, appSchema) + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/react-to-dsl/README.md
(1 hunks)packages/react-to-dsl/src/constants.ts
(1 hunks)packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx
(1 hunks)packages/react-to-dsl/test/testcases/index.test.js
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/react-to-dsl/README.md
- packages/react-to-dsl/src/constants.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/react-to-dsl/test/testcases/index.test.js (1)
packages/react-to-dsl/src/transform.ts (1)
transformReactToDsl
(223-406)
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx (1)
packages/react-to-dsl/test/testcases/002_data-binding/input/DataBindingDemo.jsx (2)
state
(6-10)utils
(12-12)
🪛 Biome (2.1.2)
packages/react-to-dsl/test/testcases/003_createVM/input/createVm.jsx
[error] 80-80: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 84-84: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 136-137: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 148-148: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 162-162: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 228-229: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 235-235: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 360-360: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
[error] 435-435: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 445-445: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 448-449: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 451-452: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 459-459: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 463-463: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
[error] 470-470: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个result你看看能不能换个名字。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK,gitignore这里应该不需要result了,我删掉吧。
feat: introduce react-to-dsl plugin
English | 简体中文
PR
PR Checklist
PR Type
Background and solution
What is the current behavior?
项目缺少从 React 源码到 Tiny Engine DSL 的转换能力。
What is the new behavior?
新增包
@opentiny/tiny-engine-react-to-dsl
,提供从 React 代码到 TinyEngine DSL(IAppSchema)的转换能力,聚焦“单文件源 -> 单个 Page/Block schema”。核心能力如下:children
树;对 JS 表达式以{ type: 'JSExpression', value }
形式保留。page.methods
;类组件提取生命周期方法与类方法。arr.map(x => <JSX />)
列表渲染,转换为子节点的loop
表达式。props.style
:对象转为 CSS 字符串。Form
->TinyForm
、Input
->TinyInput
等),并包含图标特殊映射(DatabaseOutlined
->Icon
且附加name: 'IconPanelMini'
)。page.css
。test/testcases/**/output/app.schema.json
与page.schema.json
)。简要 API:
transformReactToDsl(code: string, options?: { filename?: string; isBlock?: boolean; css?: string | string[] }): IAppSchema
Docs
Tests
test/testcases/**
,会从input/
读取 .jsx/.tsx 与 .css,输出转换结果至output/
Does this PR introduce a breaking change?
Summary by CodeRabbit
New Features
Documentation
Tests
Chores