Skip to content

Conversation

@zjy2414
Copy link

@zjy2414 zjy2414 commented Aug 24, 2025

English | 简体中文

PR Checklist

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • Built its own designer, fully self-validated (validated via unit/integration tests and app generation; no separate designer build in this PR)

PR Type

  • Feature
  • Bugfix
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

Background and solution

What is the current behavior?

  • TinyEngine lacks the ability to generate React application code from DSL schema.
  • Lack of systematic test coverage for React-generated results.

What is the new behavior?

This PR introduces a new React generator package and its tests:

  • New package: @opentiny/tiny-engine-dsl-react (packages/react-generator)
    • End-to-end React app generation from TinyEngine DSL
    • Plugin-based architecture (template, page/block, dataSource, i18n, router, utils, globalState, dependencies, formatCode, parseSchema)
    • Data binding support for JSDataBinding / JSExpression with model semantics
    • Lifecycle mapping to React component lifecycle
    • Code formatting integration
    • Typed entry points and developer README
  • Tests
    • Vitest based suite covering generator core, page/block generation, routing/i18n/utils/global store scaffolding, and component rendering paths
    • Example and case-driven testcases to validate generated outputs and structure

How to validate locally:

  • Build and run tests under react-generator
  • Optionally run the full suite that generates a sample app and diff-checks outputs

Does this PR introduce a breaking change?

  • Yes
  • No

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced React DSL-to-React code generator for low-code application development
    • Full application generation from schemas with plugin-based extensibility
    • Two-way data binding and lifecycle hook support
    • Built-in internationalization, routing, and state management
    • Automatic code formatting with Prettier and TypeScript type safety
    • Comprehensive documentation and test fixtures
  • Documentation

    • Added detailed package README with usage examples
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 24, 2025

Walkthrough

This PR introduces a comprehensive React DSL code generator package (@opentiny/tiny-engine-dsl-react) with plugin-driven architecture for converting DSL schemas into full React applications. The package includes generator core logic, 11 plugins for code generation, utilities, templates, TypeScript declarations, and extensive test fixtures covering data binding, lifecycle hooks, and end-to-end scenarios.

Changes

Cohort / File(s) Summary
Configuration & Tooling
package.json, .eslintrc.cjs, .gitignore, jsconfig.json, vite.config.mjs, README.md
Package manifest with build/test scripts; ESLint/Vite configs; project documentation with features, design philosophy, and API reference.
Core Generator
src/generator/codeGenerator.js, src/generator/generateApp.js, src/generator/generateJsx.js, src/generator/page.js, src/generator/parseImport.js, src/generator/index.js
Plugin-driven CodeGenerator class with three-phase pipeline; generateApp() factory composing plugins; JSX generation with hooks; page/block code generation; import resolution.
Constants & Type Definitions
src/constant/index.js, src/index.d.ts, src/index.js
Component mappings (Ant Design, built-in), intrinsic elements, protocol tokens; comprehensive TypeScript ambient declarations for public API.
Parsers
src/parser/jsx-slot.js, src/parser/state-type.js, src/parser/state.js
JSX template generation from slot schema; protocol-type transformation (JS expressions, functions, i18n, resources); state tree traversal and unwrapping.
Plugins
src/plugins/genDataSourcePlugin.js, src/plugins/genBlockPlugin.js, src/plugins/genDependenciesPlugin.js, src/plugins/genPagePlugin.js, src/plugins/genRouterPlugin.js, src/plugins/genUtilsPlugin.js, src/plugins/genI18nPlugin.js, src/plugins/genTemplatePlugin.js, src/plugins/formatCodePlugin.js, src/plugins/genGlobalState.js, src/plugins/parseSchemaPlugin.js, src/plugins/index.js
Eleven plugin factories for schema transformation: data sources, blocks, dependencies, pages, routing, utilities, i18n, templates, code formatting, global state (Zustand), and schema parsing.
Utilities
src/utils/formatCode.js, src/utils/generateImportStatement.js, src/utils/hasJsx.js, src/utils/mergeOptions.js, src/utils/parseRequiredBlocks.js, src/utils/upperCase.js, src/utils/index.js
Code formatting (Prettier integration); ES import statement generation; JSX detection; utility helpers (string casing, random, event handling, icon utilities).
Pre-processor & Templates
src/pre-processor/index.js, src/templates/index.js, src/templates/react-template/index.js, src/templates/react-template/template-files/*
Schema transformers (text→span, icon normalization, grid/collection handling); React project scaffold generator with Vite config, HTML entry, HTTP/i18n/store infrastructure.
Test Configuration - Data Binding
test/data-binding/index.config.js, test/data-binding/generatePage.test.js, test/data-binding/case*_*/input/page.schema.json
Test config and runner for two-way data binding scenarios; page schemas with nested state bindings (basic and complex).
Test Configuration - Full
test/full/index.config.js, test/full/generatePage.test.js, test/full/case*_*/input/*
Test config and runner for comprehensive end-to-end code generation; three scenarios: normal form table, prop-accessor pattern, state-accessor pattern; includes page schemas, block schemas, component mappings.
Test Configuration - Lifecycle
test/lifecycle/index.config.js, test/lifecycle/lifecycle.test.js, test/lifecycle/mockData.js
Lifecycle hook validation test suite; four mock schemas covering basic, complex, mixed, and error-boundary lifecycle scenarios.
Test Configuration - Generator
test/generator/generateApp.test.js, test/generator/mockData.js, test/generator/expected/appdemo01/**
End-to-end app generation test with comprehensive mock schema; expected output fixture directory containing generated project structure (router, views, stores, i18n, HTTP, lowcodeConfig, utils).
Test Utilities & Format
test/formatCode/formatCode.test.js, test/unit/hasJsx.test.js, test/unit/parseRequiredBlocks.test.js, utils/logDiffResult.js
Prettier formatting validation across JS/JSX/JSON/CSS/HTML; JSX detection unit tests; block parsing unit tests; diff result logging utility.

Sequence Diagram(s)

sequenceDiagram participant User participant generateApp as generateApp<br/>(config) participant CodeGen as CodeGenerator participant Plugins as Plugins participant Context as Context API User->>generateApp: Provide config<br/>(pluginConfig,<br/>customPlugins,<br/>customContext) generateApp->>Plugins: Instantiate<br/>default plugins Plugins->>generateApp: Plugin instances generateApp->>CodeGen: Create with<br/>transformStart,<br/>transform,<br/>transformEnd phases activate CodeGen User->>CodeGen: generate(schema) Note over CodeGen: Reset state CodeGen->>CodeGen: transformStart loop Plugin Execution CodeGen->>Plugins: Execute plugin.run() Plugins->>Context: addFile/addLog/getFile Context->>CodeGen: Update genResult end CodeGen->>CodeGen: transform CodeGen->>CodeGen: transformEnd alt Success CodeGen-->>User: {errors: [], genResult, genLogs} else Error rect rgb(200, 50, 50) Note over CodeGen: Tolerant error handling CodeGen-->>User: {errors: [err], genResult, genLogs} end end deactivate CodeGen 
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • Generator core logic (src/generator/page.js, src/generator/codeGenerator.js): Dense state management, lifecycle hook transformation, and JSX generation with multiple binding types (v-model, expressions, i18n, resources, slots).
  • Plugin implementations (src/plugins/gen*.js): Router generation with lazy loading; global state (Zustand) store generation; dependency resolution with version alignment; complex schema traversal and code assembly.
  • Parser modules (src/parser/jsx-slot.js, src/parser/state-type.js): Protocol-type protocol handling, nested binding transformations, icon/component name normalization.
  • Template scaffold (src/templates/react-template/template-files/src/lowcodeConfig/dataSource.js): Runtime data-source loader with interceptor wiring, mock support, and dynamic function evaluation.
  • Test fixtures (test/generator/mockData.js, test/full/case*/input/page.schema.json): Large, complex schema definitions requiring careful validation against expected generator output.

Suggested labels

test, generator, plugin-system, dsl, react, code-generation

Suggested reviewers

  • Zcating
  • hexqi

🐰 A generator born, with plugins galore,
DSL to React, code flows out the door,
Schemas transform through phases so grand,
Bindings and lifeCycles—a developer's hand!
Tests dance through data, lifecycle, and more,
Low-code magic opens opportunity's door!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The PR title "feat: introduce react-generator plugin" refers to a real and significant aspect of the changeset—the introduction of a plugin-based architecture for code generation. However, the title understates the actual scope of the change. The PR introduces a complete React code generator package (@opentiny/tiny-engine-dsl-react) with multiple subsystems including a plugin infrastructure, 11 concrete plugins (template, page, block, dataSource, i18n, router, utilities, globalState, dependencies, formatCode, parseSchema), JSX generation with data binding support, schema parsing, lifecycle mapping, comprehensive utilities, and a full test suite. The title's phrasing is somewhat ambiguous about whether it refers to a single plugin, the plugin system itself, or the overall generator, and it does not clearly convey the breadth and main purpose of this addition—enabling end-to-end DSL-to-React code generation. Consider clarifying the title to better reflect the main contribution, such as "feat: add @opentiny/tiny-engine-dsl-react code generator with plugin architecture" or "feat: introduce React DSL-to-code generator package". This would make the primary purpose and scope of the PR immediately clear to someone reviewing the commit history, rather than leaving ambiguity about whether the PR adds a single plugin, introduces a plugin system, or adds a complete generator package.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added enhancement New feature or request ospp ospp labels Aug 24, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 101

♻️ Duplicate comments (4)
packages/react-generator/test/generator/expected/appdemo01/src/http/axios.js (4)

129-135: Mirror disableMock explicit restore for linter parity.


116-128: Support single mock route objects here too.


136-137: AbortController note applies to expected outputs as well.


21-54: Mirror axios wrapper fixes in test expectations to keep tests green.

Duplicate of the template change: apply the same polyfill hardening (no this alias, guard error.response, remove unused expression, rename config to reqConfig).

Also applies to: 31-37

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 87

♻️ Duplicate comments (40)
packages/react-generator/src/utils/uaperCase.js (1)

1-4: Fix filename typo and harden the util (type guard + param name)

The file name “uaperCase.js” looks misspelled; also, using string as a parameter name is confusing and there’s no type guard. Please rename the file to something like capitalizeFirstLetter.js (or capitalize.js) and update imports accordingly. Add a light type check to avoid surprising errors if a non-string slips through.

Inline change:

-export const capitalizeFirstLetter = (string) => { - if (!string) return string // 检查空字符串 - return string.charAt(0).toUpperCase() + string.slice(1) -} +export const capitalizeFirstLetter = (str) => { + if (!str) return str // empty/falsy short-circuit + if (typeof str !== 'string') return str + return str.charAt(0).toUpperCase() + str.slice(1) +}

Follow-ups:

  • Rename file (preserve history): git mv packages/react-generator/src/utils/uaperCase.js packages/react-generator/src/utils/capitalizeFirstLetter.js
  • Update imports that reference the old path.

Verification sweep:

#!/bin/bash # Find lingering references to the old filename and symbol rg -nP -C1 "from '.*/uaperCase'|uaperCase" packages rg -nP -C1 '\bcapitalizeFirstLetter\b' packages

I can push a follow-up patch that performs the rename and import updates if you want.

packages/react-generator/src/templates/react-template/template-files/src/lowcodeConfig/store.js (1)

6-8: Bug: using factory’s $id before instantiation; also no guard for non-function exports

You’re indexing with $id on the factory instead of the created instance and not guarding non-function exports. This will yield undefined/mis-keyed entries or runtime errors. Fix by iterating entries, type‑checking, instantiating, and deriving the key from the instance (with sane fallbacks).

Apply this diff:

- Object.values({ ...useDefinedStores }).forEach((store) => { - stores[store.$id] = store() - }) + Object.entries(useDefinedStores).forEach(([exportName, factory]) => { + if (typeof factory !== 'function') return + const instance = factory() + const id = instance?.$id ?? factory.$id ?? exportName + stores[id] = instance + })
packages/react-generator/test/generator/expected/appdemo01/package.json (1)

3-3: Mark the generated app as private to avoid accidental publish

Template/fixtures should not be publishable by default.

Apply this diff:

 "version": "1.0.0", + "private": true,
packages/react-generator/test/generator/expected/appdemo01/src/hooks/uselazy.jsx (1)

3-10: React.lazy misuse risk — support importer, promise, or component; add fallback

Current code breaks if a Component (not a promise) is passed, and lacks Suspense fallback. Make the API resilient to importer functions, dynamic import promises, or preloaded components.

Apply this diff:

-export const useLazy = (Component) => { - const Lazy = lazy(() => Component) - return (props) => ( - <Suspense> - <Lazy {...props} /> - </Suspense> - ) -} +export const useLazy = (importerOrComponent) => { + // Accept: + // 1) () => import('...') (preferred: true code-splitting) + // 2) import('...') promise (works with existing router usage) + // 3) Preloaded Component (wrap to module shape) + const factory = + typeof importerOrComponent === 'function' + ? importerOrComponent + : typeof importerOrComponent?.then === 'function' + ? () => importerOrComponent + : () => Promise.resolve({ default: importerOrComponent }) + const Lazy = lazy(factory) + return (props) => ( + <Suspense fallback={null}> + <Lazy {...props} /> + </Suspense> + ) +}
packages/react-generator/utils/logDiffResult.js (1)

1-32: Fix ESLint no-console violations to prevent CI failure (test helper)

This helper is console-driven; disable the rule at file scope (or move under test utils). Without this, ESLint will fail.

Apply this diff:

+/* eslint-disable no-console */ export const logDiffResult = (result) => { console.log( 'Statistics - equal entries: %s, distinct entries: %s, left only entries: %s, right only entries: %s, differences: %s', result.equal, result.distinct, result.left, result.right, result.differences )

Optional: move to packages/react-generator/test/utils/logDiffResult.js to rely on test lint overrides.

packages/react-generator/vite.config.mjs (1)

1-6: __dirname is undefined in ESM; compute from import.meta.url to avoid runtime errors.

vite.config.mjs runs as ESM, so using __dirname will throw. Compute it via fileURLToPath.

Apply:

 import { defineConfig } from 'vite' import path from 'path' +import { fileURLToPath } from 'url' import { viteStaticCopy } from 'vite-plugin-static-copy' +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + export default defineConfig({
packages/react-generator/src/templates/react-template/template-files/packageJson.js (1)

19-28: Duplicate “antd” key; last one wins — remove the earlier duplicate.

Static analysis is right: the first antd entry is overwritten by the later one. Keep a single entry.

 dependencies: { '@opentiny/tiny-engine-i18n-host': '^1.0.0', // '@opentiny/react': 'latest', // '@opentiny/react-icon': 'latest', axios: 'latest', - antd: 'latest', 'axios-mock-adapter': '^1.19.0', react: '^18.3.1', 'react-dom': '^18.3.1', 'react-router-dom': '^6.27.0', '@vitejs/plugin-react': 'latest', "@ant-design/icons": "^6.0.0", "antd": "5.0.0", },
packages/react-generator/package.json (3)

29-33: Move vitest to devDependencies (test-only).

It doesn’t belong in runtime dependencies.

 "dependencies": { "@opentiny/tiny-engine-builtin-component": "workspace:*", - "react-router-dom": "^6.27.0", - "vitest": "^1.0.0" + "react-router-dom": "^6.27.0" }, "devDependencies": { + "vitest": "^1.0.0",

34-49: Prettier is imported at runtime by the formatter—place it under dependencies.

Your formatCode utility loads Prettier at runtime; keeping it in devDependencies will break consumers.

- "devDependencies": { + "dependencies": { + "prettier": "^2.6.1", + "react-router-dom": "^6.27.0" + }, + "devDependencies": { "@babel/eslint-parser": "^7.28.0", @@ - "prettier": "^2.6.1", "vite": "^2.8.6",
#!/bin/bash # Confirm runtime Prettier usage in generator source rg -nP "from ['\"]prettier['\"]|require\\(['\"]prettier['\"]\\)" packages/react-generator/src || true

1-11: Add TypeScript declarations and an ESM/CJS-aware exports map
Consumers installing this package today won’t get typings by default, nor will modern bundlers or Node know which entrypoints to load for ESM vs. CJS.

• Add a top-level "types" entry pointing to your declaration file (e.g. "dist/index.d.ts").
• Define an "exports" map so that:
import loads the ES build,
require loads the CJS build,
types points to your .d.ts file, and
default provides a fallback.
• Verify your build pipeline actually emits/copies index.d.ts into dist/.

 { "name": "@opentiny/tiny-engine-dsl-react", "version": "0.1.0", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/tiny-engine-dsl-react.es.js", + "require": "./dist/tiny-engine-dsl-react.cjs.js", + "default": "./dist/tiny-engine-dsl-react.es.js" + } + }, "publishConfig": { "access": "public" }, "main": "dist/tiny-engine-dsl-react.cjs.js", "module": "dist/tiny-engine-dsl-react.es.js", "files": [ "dist" ],

Ensure you update your build (e.g. tsc --declaration) or add a post-build step to emit/copy index.d.ts into dist/.

packages/react-generator/src/constant/index.js (1)

395-399: Icon prefix constant is empty — breaks icon detection/normalization

With TINY_ICON set to '', any startsWith(TINY_ICON) check is trivially true. This prevents correct detection and normalization of icon component names and will skew import/usage logic.

Apply this diff to set a concrete prefix and align the comment with the actual convention:

-/** - * 图标组件名,统一前缀为 TinyIcon,与从组件库引入的方法名 iconXxx 区分开,暂时不使用 - */ -const TINY_ICON = '' +/** + * 图标组件统一前缀(与 iconXxx 方法名区分)。和预处理保持一致:Tiny${name} + */ +const TINY_ICON = 'Tiny'

If the desired final form is TinyIcon${name}, update both here and the pre-processor to use 'TinyIcon'.

packages/react-generator/test/generator/expected/appdemo01/src/App.jsx (1)

1-11: Router file downstream uses invalid redirect prop and stray commas (React Router v6)

This App looks fine, but the referenced router has issues (invalid redirect prop; extra commas). Track/fix in the router file; otherwise App boot will throw. Comment previously raised; echoing here for visibility.

Use this to spot invalid props/comma artifacts:

#!/bin/bash rg -nC2 --glob "packages/**/expected/**/src/router/index.jsx" -P '\bredirect\s*=|,\s*<Route'
packages/react-generator/test/generator/expected/appdemo01/vite.config.js (1)

16-18: Security/privacy: leaking all host environment variables into the client bundle

Defining define: { 'process.env': { ...process.env } } inlines every env var (including secrets) into the browser bundle. Vite already exposes whitelisted vars as import.meta.env (prefixed by VITE_ by default). Remove this or strictly whitelist.

Minimal safe change:

- define: { - 'process.env': { ...process.env } - }, + // Use import.meta.env (Vite) for client-readable env; avoid leaking host env. + define: { + 'process.env': {} + },

If you must support legacy process.env.X in app code, inject only selected keys:

- define: { - 'process.env': { ...process.env } - }, + define: { + 'process.env': Object.fromEntries( + Object.entries(process.env) + .filter(([k]) => /^VITE_/.test(k)) // whitelist + .map(([k, v]) => [k, JSON.stringify(v ?? '')]) + ) + },

Remember to update both the template emitter and the expected fixture together.

packages/react-generator/src/templates/react-template/template-files/src/hooks/useLazy.jsx (2)

1-10: Unify filename/import casing: useLazy.jsx vs uselazy.jsx

There are imports elsewhere referencing ../hooks/uselazy. On case-sensitive filesystems this will fail with the current filename useLazy.jsx. Standardize on useLazy across generator and fixtures.

Run to verify and patch callsites:

#!/bin/bash # Find hook files fd -a -t f '(?i)uselazy\.jsx$' packages || true # Find imports referencing the hook (case-insensitive) rg -n -i -C1 "from ['\"][^'\"]*/hooks/(useLazy|uselazy)['\"]|import\s+\{\s*useLazy\s*\}" packages/react-generator # Optional: show generated fixtures referencing uselazy rg -n -i "hooks/uselazy" packages/react-generator/test || true

3-10: Fix React.lazy usage and add Suspense fallback to avoid runtime errors

lazy(() => Component) is invalid unless Component is a Promise resolving to a module with a default export. This will break when a plain component function/class or an import loader function is passed. Also, Suspense should have a fallback. Make the hook accept either a loader function () => import('...') or a promise, normalize it, and provide a configurable fallback.

-import { lazy, Suspense } from 'react' - -export const useLazy = (Component) => { - const Lazy = lazy(() => Component) - return (props) => ( - <Suspense> - <Lazy {...props} /> - </Suspense> - ) -} +import { lazy, Suspense } from 'react' + +// Accepts either: +// - a function returning a dynamic import () => import('...'), or +// - a dynamic import Promise (import('...')), or +// - a component value (will be wrapped as default export) +export const useLazy = (loaderOrComponent, { fallback = null } = {}) => { + const factory = + typeof loaderOrComponent === 'function' + ? loaderOrComponent + : () => loaderOrComponent + + const Lazy = lazy(() => + Promise.resolve(factory()).then((mod) => + mod && typeof mod === 'object' && 'default' in mod ? mod : { default: mod } + ) + ) + + return (props) => ( + <Suspense fallback={fallback}> + <Lazy {...props} /> + </Suspense> + ) +}
packages/react-generator/src/templates/react-template/template-files/src/http/index.js (1)

19-24: Don’t swallow Axios errors; guard optional fields and rethrow

error.response can be undefined (network/CORS). Accessing response.status would throw, and not returning a rejection causes the promise chain to resolve with undefined.

- http.interceptors.response.use(dataHandler, (error) => { - const response = error.response - if (response.status === 403 && response.headers && response.headers['x-login-url']) { - // TODO 处理无权限时,重新登录再发送请求 - } - }) + http.interceptors.response.use( + dataHandler, + (error) => { + const response = error?.response + if (response?.status === 403 && response.headers?.['x-login-url']) { + // TODO: 处理无权限时,触发登录并可选重试原请求 + // 例如: + // const loginUrl = response.headers['x-login-url'] + // await redirectToLogin(loginUrl) + // return http.request(error.config) + } + return Promise.reject(error) + } + )
packages/react-generator/test/generator/expected/appdemo01/src/http/index.js (1)

19-24: Mirror fix in generated fixture: guard and rethrow errors

Same interceptor issue as the template: optional-chain error.response and return Promise.reject(error) to propagate failures.

- http.interceptors.response.use(dataHandler, (error) => { - const response = error.response - if (response.status === 403 && response.headers && response.headers['x-login-url']) { - // TODO 处理无权限时,重新登录再发送请求 - } - }) + http.interceptors.response.use( + dataHandler, + (error) => { + const response = error?.response + if (response?.status === 403 && response.headers?.['x-login-url']) { + // TODO 处理无权限时,重新登录再发送请求 + } + return Promise.reject(error) + } + )
packages/react-generator/test/generator/expected/appdemo01/src/stores/testState.js (2)

8-13: Zustand setter is a no-op — state never updates (inner function not invoked).

The callback passed to set defines an inner function but never calls it; with plain Zustand (no immer middleware), mutations inside set’s callback won’t apply unless you return a new partial. Update to a parameterized setter that performs a partial update.

Apply this diff:

- setAge: () => - set((state) => { - function setAge(age) { - state.age = age - } - }), + setAge: (age) => set({ age }),

14-19: Same bug for setName — replace with a proper partial update.

Mirror the setAge fix so updates actually apply.

- setName: () => - set((state) => { - function setName(name) { - state.name = name - } - }) + setName: (name) => set({ name })
packages/react-generator/src/plugins/genTemplatePlugin.js (1)

8-19: Standardize plugin behavior: always mutate context.genResult and also return files.

Current branches either return files or push into context.genResult, leading to inconsistent behavior and potential drops. Unify: resolve the chosen template (options.template > context.template > 'default'), normalize results to an array, push to context.genResult, and return the same array. Also pass context when calling template functions for symmetry.

- run(schema, context) { - if (typeof options?.template === 'function') { - const res = options.template(schema, context) - if (Array.isArray(res)) { - return res - } - - if (res?.fileContent && res?.fileName) { - return res - } - - return - } - - const template = context?.template || 'default' - - if (!template) { - return - } - - if (typeof template === 'function') { - context.genResult.push(...(template(schema) || [])) - return - } - - if (templateMap[template]) { - context.genResult.push(...templateMap[template](schema)) - } - } + run(schema, context) { + const chosen = options?.template ?? context?.template ?? 'default' + if (!chosen) return + + const callTemplate = (tpl) => tpl?.(schema, context) + const res = + typeof chosen === 'function' + ? callTemplate(chosen) + : (templateMap[chosen] && callTemplate(templateMap[chosen])) || [] + + const files = Array.isArray(res) ? res : res?.fileName ? [res] : [] + if (files.length) { + context.genResult.push(...files) + } + return files + }

Also applies to: 27-35

packages/react-generator/src/plugins/genPagePlugin.js (2)

21-24: Guard against missing/malformed schema inputs

Run currently assumes schema.pageSchema exists and is iterable, which can throw.

Apply this diff:

- run(schema) { - const pages = schema.pageSchema - const componentsMap = schema.componentsMap || [] + run(schema = {}) { + const { pageSchema = [], componentsMap = [] } = schema || {} + const pages = Array.isArray(pageSchema) ? pageSchema : []

27-39: Only mark a single entry page

isEntry: true for all pages is incorrect. Respect page flag if present; otherwise default to the first page.

Apply this diff:

- pages.forEach((pageSchema) => { + pages.forEach((pageSchema, index) => { const fileName = pageSchema.fileName const pageInfo = { schema: pageSchema, name: fileName } // 使用 generatePageCode 生成页面代码 - const pagePanels = generatePageCode({  + const pagePanels = generatePageCode({  pageInfo, componentsMap, - isEntry: true  + isEntry: Boolean(pageSchema.isEntry) || index === 0  })
packages/react-generator/src/pre-processor/index.js (1)

33-38: Icon prefix logic breaks when TINY_ICON is empty; add fallback

TINY_ICON is currently an empty string; startsWith('') is always true, so no prefix will be added. Use a non-empty fallback (e.g., 'TinyIcon').

Apply this diff:

- if (componentName === BUILTIN_COMPONENT_NAME.ICON && props.name) { - // 图标组件名,统一前缀为 TinyIcon,与从组件库引入的方法名 iconXxx 区分开 - const iconName = props.name.startsWith(TINY_ICON) ? props.name : `Tiny${props.name}` + if (componentName === BUILTIN_COMPONENT_NAME.ICON && props.name) { + // 图标组件名,统一前缀为 TinyIcon,与从组件库引入的方法名 iconXxx 区分开 + const prefix = TINY_ICON || 'TinyIcon' + const iconName = props.name.startsWith(prefix) ? props.name : `${prefix}${props.name}` schema.componentName = iconName delete props.name }
packages/react-generator/src/plugins/parseSchemaPlugin.js (3)

13-18: Guard against missing/invalid pageSchema and avoid unconditional mutation of componentsMap

This will throw if pageSchema is missing. Also, blindly spreading schema.componentsMap assumes it’s an array and can duplicate built-ins.

- run(schema) { - const { pageSchema } = schema + run(schema) { + const { pageSchema } = schema || {} + if (!Array.isArray(pageSchema) || pageSchema.length === 0) { + return + } const pagesMap = {} const resPageTree = [] - schema.componentsMap = [...schema.componentsMap, ...BUILTIN_COMPONENTS_MAP] + const mapByName = new Map((schema.componentsMap || []).map((c) => [c.componentName, c])) + for (const c of BUILTIN_COMPONENTS_MAP) mapByName.set(c.componentName, c) + schema.componentsMap = Array.from(mapByName.values())

28-31: Deep-clone meta to avoid mutating source items

Spreading componentItem keeps the same meta reference; later mutations to newComponentItem.meta will leak back.

- const newComponentItem = { - ...componentItem - } + const newComponentItem = { + ...componentItem, + meta: { ...(componentItem.meta || {}) } + }

35-42: Parent-chain walk lacks existence checks; can throw on broken references

If a parentId is missing from pagesMap, preFolder is undefined, and preFolder.meta will throw.

- while (curParentId !== '0' && depth < 1000) { - const preFolder = pagesMap[curParentId] - - path = `${preFolder.meta.name}${path ? '/' : ''}${path}` - newComponentItem.meta.router = `${preFolder.meta.router}/${newComponentItem.meta.router}` - curParentId = preFolder.meta.parentId - depth++ - } + while (curParentId !== '0' && depth < 1000) { + const preFolder = pagesMap[curParentId] + if (!preFolder || !preFolder.meta) break + + const seg = preFolder.meta.name || '' + path = `${seg}${path ? '/' : ''}${path}` + const parentRouter = preFolder.meta.router || '' + const childRouter = newComponentItem.meta.router || '' + newComponentItem.meta.router = `${parentRouter}/${childRouter}`.replace(/\/+/g, '/') + curParentId = preFolder.meta.parentId + depth++ + }
packages/react-generator/src/generator/page.js (4)

26-26: Fix typo in import path: uaperCase → upperCase

-import { capitalizeFirstLetter } from '../utils/uaperCase' +import { capitalizeFirstLetter } from '../utils/upperCase'

Note: ensure the file is renamed to utils/upperCase.js to match the import.

Run to find all references to the misspelled path and update them:

#!/bin/bash rg -n "../utils/uaperCase" -C1

96-96: Resolve "used before defined" mutual recursion between recurseJSXChildren and generateJSXNode

Minimal change: forward-declare the variable and convert the later const to an assignment.

 // 工具函数:处理JSX子元素 -const recurseJSXChildren = (children, state, description, result, isClassComponent = false) => { +let generateJSXNode +const recurseJSXChildren = (children, state, description, result, isClassComponent = false) => {
-const generateJSXNode = (schema, state, description, isRootNode = false, isClassComponent = false) => { +generateJSXNode = (schema, state, description, isRootNode = false, isClassComponent = false) => {

Also applies to: 274-274


192-192: Remove unused parameters from handleJSXDataBinding and its call site

-const handleJSXDataBinding = ({ key, item, attrsArr, description, state, isClassComponent = false, hasOnChange = false, componentName = '' }) => { +const handleJSXDataBinding = ({ key, item, attrsArr, isClassComponent = false, hasOnChange = false, componentName = '' }) => {
- const result = handleJSXDataBinding({key, item, attrsArr, description, state, isClassComponent, hasOnChange, componentName}) + const result = handleJSXDataBinding({ key, item, attrsArr, isClassComponent, hasOnChange, componentName })

Also applies to: 245-246


437-440: Replace require('path') with ES module import

- const path = require('path') - const relativePath = path?.relative(fromPath, toPath).replace(/\\/g, '/') + const relativePath = path.relative(fromPath, toPath).replace(/\\/g, '/')

And add the import near the top:

 import { getTypeOfSchema, avoidDuplicateString, toPascalCase, prettierOpts, isOn, addAccessorRecord, addIconRecord, handleIconInProps, getFunctionInfo, formatCode } from '../utils' +import path from 'path'

Also applies to: 13-24

packages/react-generator/src/generator/generateJsx.js (1)

141-151: Uncomment and implement config setters to make hooks effective

These setters are currently no-ops; update to merge the provided config objects.

 setScriptConfig: (newConfig) => { if (!newConfig || typeof newConfig !== 'object') { return } - - // scriptConfig = { - // ...scriptConfig, - // ...newConfig - // } + Object.assign(scriptConfig, newConfig) },
 setStyleConfig: (newConfig = {}) => { if (!newConfig || typeof newConfig !== 'object') { return } - - // styleConfig = { - // ...styleConfig, - // ...newConfig - // } + Object.assign(styleConfig, newConfig) },

Also applies to: 152-162

packages/react-generator/src/plugins/genGlobalState.js (2)

62-64: Replace all occurrences of this. → state. and support function bodies.

.replace('this.', 'state.') only replaces the first occurrence and misses nested access. Use a global replacement; consider parsing function signatures if you need to retain parameters.

Apply minimal safe fix:

- ([key, value]) => `${key}: () => set((state) => { - ${value.value.replace('this.', 'state.')} - })` + ([key, value]) => `${key}: () => set((state) => { + ${value.value.replace(/this\./g, 'state.')} + })`

If you need parameter support, I can provide a robust transform using getFunctionInfo similar to your parser utilities.


51-55: Fix value-serialization logic (falsy primitives, objects, and strings).

The current condition if (value || typeof value === 'object') is inconsistent (e.g., 0 and '' bypass JSON.stringify, while truthy primitives are stringified). Serialize by type instead, preserving numbers/booleans, quoting strings, and JSON-stringifying objects/null.

Apply:

- if (value || typeof value === 'object') { - value = JSON.stringify(value) - } + if (value === null) { + value = 'null' + } else if (typeof value === 'object') { + value = JSON.stringify(value) + } else if (typeof value === 'string') { + // Preserve quotes correctly; JSON.stringify handles embedded quotes/escapes. + value = JSON.stringify(value) + } else if (typeof value === 'number' || typeof value === 'boolean') { + // keep as-is + } else if (typeof value === 'undefined') { + value = 'undefined' + }
packages/react-generator/src/parser/jsx-slot.js (5)

113-113: ESLint: replace short-circuit push with explicit if

Satisfy no-unused-expressions and improve readability.

- condition && result.push(' }') + if (condition) { + result.push(' }') + }

24-29: Event args forwarding bug: passes a single array instead of variadic args

Spread the collected args when invoking the handler.

- const extendParams = item.params.join(',') - - return attrsArr.push(`${key}={(...eventArgs) => ${propValue}(eventArgs, ${extendParams})}`) + const extendParams = item.params.join(',') + return attrsArr.push(`${key}={(...eventArgs) => ${propValue}(...eventArgs, ${extendParams})}`)

34-60: Improve binding handler: support JSDataBinding; avoid map-for-side-effects; fix literals

  • Include JSDataBinding as an expression type.
  • Use forEach instead of map for side effects.
  • Use JSON.stringify for strings; omit false booleans.
-function handleBinding(props, attrsArr, description) { - Object.entries(props).map(([key, item]) => { +function handleBinding(props, attrsArr, description) { + Object.entries(props).forEach(([key, item]) => { const propType = item?.type || 'literal' - if (['JSExpression', 'JSFunction'].includes(propType)) { + if (['JSExpression', 'JSFunction', 'JSDataBinding'].includes(propType)) { return handleExprBinding(key, item, attrsArr) } @@ - if (typeof item === 'string') return attrsArr.push(`${key}="${item}"`) - if (typeof item === 'boolean') return attrsArr.push(item ? key : '') + if (typeof item === 'string') return attrsArr.push(`${key}=${JSON.stringify(item)}`) + if (typeof item === 'boolean') { + if (item) attrsArr.push(key) + return + } } @@ - return attrsArr.push(`${key}={${JSON.stringify(item)}}`) - }) + return attrsArr.push(`${key}={${JSON.stringify(item)}}`) + }) }

74-76: Icon detection runs for every component because TINY_ICON is ''

Guard against empty prefix; otherwise startsWith('') is always true.

- if (componentName?.startsWith(TINY_ICON)) { + if (TINY_ICON && componentName?.startsWith(TINY_ICON)) { addIconRecord(description, componentName) }

100-109: Children array: handle strings and simple expressions without recursion crash

When children include strings or JSExpression/JSDataBinding nodes, directly render them instead of destructuring inside recursive calls.

- if (Array.isArray(children)) { - const subTemplate = children.map((child) => generateJSXTemplate(child, description)).join('') - result.push(subTemplate) - } else if (children?.type === 'JSExpression') { + if (Array.isArray(children)) { + const subTemplate = children + .map((child) => { + if (typeof child === 'string') return child + if (child?.type === 'JSExpression' || child?.type === 'JSDataBinding') { + return `{ ${child.value.replace(/this\./g, '')} }` + } + if (child?.type === 'i18n') { + return `{t('${child.key}')}` + } + return generateJSXTemplate(child, description) + }) + .join('') + result.push(subTemplate) + } else if (children?.type === 'JSExpression') { result.push(`{ ${children.value.replace(/this\./g, '')} }`) } else if (children?.type === 'i18n') {
packages/react-generator/test/generator/expected/appdemo01/src/http/axios.js (2)

129-133: no-unused-expressions: mock && mock.restore()

Mirror the fix suggested for the template.

- disableMock() { - mock && mock.restore() - mock = undefined - }, + disableMock() { + if (mock) { + mock.restore() + mock = undefined + } + },

31-37: no-unused-expressions: replace short-circuit with explicit assignment

Same as template: avoid && for side-effects; guard error.response.

- /* eslint-disable no-useless-call */ - typeof handleData === 'function' && (data = handleData.call(null, data, setting)) + if (typeof handleData === 'function') { + /* eslint-disable no-useless-call */ + data = handleData.call(null, data, setting) + } ... - .catch((error) => { - resolve([error.response.status, error.response.data]) - }) + .catch((error) => { + const status = error?.response?.status ?? 500 + const payload = error?.response?.data ?? { message: error?.message } + resolve([status, payload]) + })
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (11)
packages/react-generator/test/generator/expected/appdemo01/src/views/createVm/createVm.jsx (3)

184-214: Switch Table block to AntD API (remove TinyUI props).

Current props (editConfig, options, auto-resize, field, sortable) are not AntD Table props. Replace with AntD-conformant columns, dataSource, rowSelection, pagination.

- <Table - editConfig={{ trigger: 'click', mode: 'cell', showStatus: true }} - columns={[ - { type: 'radio', width: 60 }, - { field: 'employees', title: '规格名称' }, - { field: 'created_date', title: 'vCPUs | 内存(GiB)', sortable: true }, - { field: 'city', title: 'CPU', sortable: true }, - { title: '基准 / 最大带宽\t', sortable: true }, - { title: '内网收发包', sortable: true } - ]} - options={[ - { - id: '1', - name: 'GFD科技有限公司', - city: '福州', - employees: 800, - created_date: '2014-04-30 00:56:00', - boole: false - }, - { - id: '2', - name: 'WWW科技有限公司', - city: '深圳', - employees: 300, - created_date: '2016-07-08 12:36:22', - boole: true - } - ]} - style={{ marginTop: '12px', borderRadius: '0px' }} - auto-resize={true} - ></Table> + <Table + rowSelection={{ type: 'radio' }} + columns={[ + { title: '规格名称', dataIndex: 'employees' }, + { title: 'vCPUs | 内存(GiB)', dataIndex: 'created_date', sorter: true }, + { title: 'CPU', dataIndex: 'city', sorter: true }, + { title: '基准 / 最大带宽', dataIndex: 'bandwidth', sorter: true }, + { title: '内网收发包', dataIndex: 'pps', sorter: true } + ]} + dataSource={[ + { key: '1', employees: 800, created_date: '2014-04-30 00:56:00', city: '福州', bandwidth: '-', pps: '-' }, + { key: '2', employees: 300, created_date: '2016-07-08 12:36:22', city: '深圳', bandwidth: '-', pps: '-' } + ]} + pagination={false} + style={{ marginTop: '12px', borderRadius: '0px' }} + />

244-250: Replace non-AntD Form props with valid AntD API.

Props like labelCol="80px", layout={false}, label-position, label-width are invalid in AntD. Use layout="horizontal" and object labelCol/wrapperCol.

- <Form - labelCol="80px" - layout={false} - label-position="left " - label-width="150px" - style={{ borderRadius: '0px' }} - > + <Form + layout="horizontal" + labelCol={{ span: 6 }} + wrapperCol={{ span: 18 }} + style={{ borderRadius: '0px' }} + >
- <Form - labelCol="80px" - layout={false} - label-position="left " - label-width="150px" - style={{ borderRadius: '0px' }} - > + <Form + layout="horizontal" + labelCol={{ span: 6 }} + wrapperCol={{ span: 18 }} + style={{ borderRadius: '0px' }} + >
- <Form - labelCol="80px" - layout={false} - label-position="left " - label-width="150px" - style={{ borderRadius: '0px' }} - > + <Form + layout="horizontal" + labelCol={{ span: 6 }} + wrapperCol={{ span: 18 }} + style={{ borderRadius: '0px' }} + >

Also applies to: 306-312, 341-347


349-387: Add stable key to items rendered by map.

Missing key breaks reconciliation and fails lint.

- {state.dataDisk.map((item) => ( - <div style={{ marginTop: '12px', display: 'flex' }}> + {state.dataDisk.map((item) => ( + <div key={item} style={{ marginTop: '12px', display: 'flex' }}>
packages/react-generator/test/generator/mockData.js (3)

333-338: Mask token-like test data to avoid secret scanners.

Replace confirmationToken with a safe placeholder.

- confirmationToken: 'dfb2c162-351f-4f44-ad5f-8998', + confirmationToken: 'PLACEHOLDER_TOKEN',

Also applies to: 1429-1434


38-58: Duplicate key “options” overwrites the array; rename the request config.

Second options (request config) clobbers the data array. Rename to request.

 ], - options: { + request: { uri: '', method: 'GET' },

888-893: Drop duplicate Form “layout”; the latter overwrites the former.

Keep a single intended value (e.g., 'top').

- layout: 'top', - layout: false, + layout: 'top',
- layout: 'top', - layout: false, + layout: 'top',
- layout: 'top', - layout: false, + layout: 'top',

Also applies to: 1027-1032, 1102-1107

packages/react-generator/src/generator/page.js (5)

26-26: Fix typo in import path

The import path has a typo - uaperCase should be upperCase.

-import { capitalizeFirstLetter } from '../utils/uaperCase' +import { capitalizeFirstLetter } from '../utils/upperCase'

437-438: Replace require() with ES6 import

Use ES6 import syntax instead of CommonJS require for consistency with the rest of the codebase.

Add the import at the top of the file with other imports:

+import path from 'path'

Then update the usage:

- const path = require('path') - const relativePath = path?.relative(fromPath, toPath).replace(/\\/g, '/') + const relativePath = path.relative(fromPath, toPath).replace(/\\/g, '/')

97-134: Resolve mutual recursion between functions

The mutual recursion between recurseJSXChildren and generateJSXNode causes an ESLint error. While this works at runtime due to hoisting, it's better to reorganize the code.

Consider using a forward declaration pattern:

+// Forward declaration to resolve mutual recursion +let generateJSXNode + // 工具函数:处理JSX子元素 const recurseJSXChildren = (children, state, description, result, isClassComponent = false) => { // ... existing code ... } // ... other helper functions ... // 工具函数:生成JSX节点 -const generateJSXNode = (schema, state, description, isRootNode = false, isClassComponent = false) => { +generateJSXNode = (schema, state, description, isRootNode = false, isClassComponent = false) => { // ... existing code ... }

192-230: Remove unused parameters

The description and state parameters are declared but never used in the handleJSXDataBinding function.

-const handleJSXDataBinding = ({ key, item, attrsArr, description, state, isClassComponent = false, hasOnChange = false, componentName = '' }) => { +const handleJSXDataBinding = ({ key, item, attrsArr, isClassComponent = false, hasOnChange = false, componentName = '' }) => {

Also update the call site on line 245:

- const result = handleJSXDataBinding({key, item, attrsArr, description, state, isClassComponent, hasOnChange, componentName}) + const result = handleJSXDataBinding({key, item, attrsArr, isClassComponent, hasOnChange, componentName})

147-151: Fix event handler to spread arguments correctly

The current implementation passes eventArgs as a single array argument instead of spreading it, which may break handlers expecting individual event arguments.

 if (item.params?.length) { const extendParams = item.params.join(',') - return `${key}={(...eventArgs) => ${eventHandler}(eventArgs, ${extendParams})}` + return `${key}={(...eventArgs) => ${eventHandler}(...eventArgs, ${extendParams})}` } else {
🧹 Nitpick comments (7)
packages/react-generator/test/generator/expected/appdemo01/src/views/createVm/createVm.jsx (2)

79-86: Stop using the children prop; write JSX children.

Biome flags noChildrenProp. Prefer element children for readability and consistency.

- <Typography.Text - children="温馨提示:页面左上角切换区域" - style={{ color: '#8a8e99', fontSize: '12px' }} - ></Typography.Text> + <Typography.Text style={{ color: '#8a8e99', fontSize: '12px' }}> + 温馨提示:页面左上角切换区域 + </Typography.Text> - <Typography.Text - children="不同区域的云服务产品之间内网互不相通;请就近选择靠近您业务的区域,可减少网络时延,提高访问速度" - style={{ display: 'block', color: '#8a8e99', borderRadius: '0px', fontSize: '12px' }} - ></Typography.Text> + <Typography.Text style={{ display: 'block', color: '#8a8e99', borderRadius: '0px', fontSize: '12px' }}> + 不同区域的云服务产品之间内网互不相通;请就近选择靠近您业务的区域,可减少网络时延,提高访问速度 + </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="通用计算型 | Si2.large.2 | 2vCPUs | 4 GiB" - style={{ fontWeight: '700' }} - ></Typography.Text> + <Typography.Text style={{ fontWeight: '700' }}> + 通用计算型 | Si2.large.2 | 2vCPUs | 4 GiB + </Typography.Text>
- <Typography.Text children="购买量" style={{ marginRight: '10px' }}></Typography.Text> + <Typography.Text style={{ marginRight: '10px' }}>购买量</Typography.Text> @@ - <Typography.Text children="台"></Typography.Text> + <Typography.Text>台</Typography.Text> @@ - <Typography.Text children="配置费用" style={{ fontSize: '12px' }}></Typography.Text> + <Typography.Text style={{ fontSize: '12px' }}>配置费用</Typography.Text> - <Typography.Text - children="¥1.5776" - style={{ paddingLeft: '10px', color: '#de504e' }} - ></Typography.Text> + <Typography.Text style={{ paddingLeft: '10px', color: '#de504e' }}> + ¥1.5776 + </Typography.Text> - <Typography.Text children="/小时" style={{ fontSize: '12px' }}></Typography.Text> + <Typography.Text style={{ fontSize: '12px' }}>/小时</Typography.Text> @@ - <Typography.Text - children="参考价格,具体扣费请以账单为准。" - style={{ fontSize: '12px', borderRadius: '0px' }} - ></Typography.Text> + <Typography.Text style={{ fontSize: '12px', borderRadius: '0px' }}> + 参考价格,具体扣费请以账单为准。 + </Typography.Text> - <Typography.Text - children="了解计费详情" - style={{ fontSize: '12px', color: '#344899' }} - ></Typography.Text> + <Typography.Text style={{ fontSize: '12px', color: '#344899' }}> + 了解计费详情 + </Typography.Text>

Also applies to: 216-223, 423-452


420-471: Use numeric spans for AntD Grid.

span should be a number, not a string.

- <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-generator/test/generator/mockData.js (2)

83-86: Typo in function name inside data handler string.

dataHanlderdataHandler (cosmetic but reduces confusion).

- value: 'function dataHanlder(res){\n return res;\n}' + value: 'function dataHandler(res){\n return res;\n}'

773-829: Align Table schema with target AntD mapping (generator responsibility).

DSL uses TinyUI-like props (editConfig, options, field, sortable, 'auto-resize'). Ensure the React generator plugin normalizes these to AntD (columns[{ title, dataIndex, sorter }], dataSource, rowSelection, pagination). If keeping DSL as-is, the generator should transform at emit time to avoid leaking non-AntD props into output.

If desired, I can patch the generator’s Table emitter to perform this normalization and update the tests accordingly. Confirm the intended target (AntD) and I’ll send a focused PR diff.

packages/react-generator/src/generator/page.js (3)

338-340: Expand void elements list for completeness

The current list of void elements is incomplete. Consider adding other HTML5 void elements to ensure proper self-closing tag handling.

- const VOID_ELEMENTS = ['img', 'input', 'br', 'hr', 'link'] + const VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']

525-534: Improve setState conversion for function components

The regex-based conversion from class-style this.setState to hooks may miss complex cases or nested setState calls.

Consider a more robust conversion approach that handles nested objects and function-based setState:

 const convertedBody = body - .replace(/this\.setState\(\s*\{\s*(\w+):\s*([^}]+)\s*\}\s*\)/g, (match, stateName, value) => { - const setterName = `set${stateName.charAt(0).toUpperCase()}${stateName.slice(1)}` - return `${setterName}(${value})` - }) + .replace(/this\.setState\(\s*\{\s*(\w+):\s*([^}]+)\s*\}\s*\)/g, (match, stateName, value) => { + return `setState(prev => ({ ...prev, ${stateName}: ${value} }))` + }) + .replace(/this\.setState\(\s*prev\s*=>\s*\(([\s\S]+?)\)\s*\)/g, (match, stateUpdate) => { + return `setState(prev => (${stateUpdate}))` + })

569-585: Consider lifecycle method compatibility

The mapping of lifecycle methods to hooks looks correct, but note that shouldComponentUpdate doesn't have a direct hooks equivalent. Using useMemo may not provide the same optimization behavior.

Would you like me to suggest a more appropriate hooks pattern for shouldComponentUpdate optimization, perhaps using React.memo with a custom comparison function?

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 0161b01 and f3d307f.

📒 Files selected for processing (3)
  • packages/react-generator/src/generator/page.js (1 hunks)
  • packages/react-generator/test/generator/expected/appdemo01/src/views/createVm/createVm.jsx (1 hunks)
  • packages/react-generator/test/generator/mockData.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/react-generator/src/generator/page.js (7)
packages/react-generator/src/templates/react-template/index.js (1)
  • value (30-30)
packages/react-generator/src/utils/index.js (15)
  • description (128-128)
  • result (26-26)
  • result (48-48)
  • result (56-56)
  • iconName (146-146)
  • handleIconInProps (145-152)
  • avoidDuplicateString (55-65)
  • isOn (92-92)
  • toPascalCase (75-75)
  • exportName (131-131)
  • addIconRecord (127-136)
  • addAccessorRecord (109-117)
  • getFunctionInfo (24-39)
  • getTypeOfSchema (22-22)
  • prettierOpts (83-87)
packages/react-generator/src/constant/index.js (6)
  • BUILTIN_COMPONENT_NAME (385-393)
  • IntrinsicElements (408-587)
  • AntdComponents (291-380)
  • TINY_ICON (398-398)
  • DEFAULT_COMPONENTS_MAP (17-264)
  • BUILTIN_COMPONENTS_MAP (267-289)
packages/react-generator/src/pre-processor/index.js (8)
  • iconName (35-35)
  • schema (16-16)
  • schema (31-31)
  • schema (44-44)
  • schema (54-54)
  • schema (64-64)
  • schema (79-79)
  • preProcess (78-92)
packages/react-generator/src/parser/state.js (2)
  • unwrapExpression (34-37)
  • translateHookState (43-57)
packages/react-generator/src/utils/uaperCase.js (2)
  • capitalizeFirstLetter (1-4)
  • capitalizeFirstLetter (1-4)
packages/react-generator/src/utils/formatCode.js (1)
  • formatCode (113-136)
packages/react-generator/test/generator/expected/appdemo01/src/views/createVm/createVm.jsx (1)
packages/react-generator/test/generator/expected/appdemo01/src/router/index.jsx (1)
  • CreateVm (6-6)
🪛 Biome (2.1.2)
packages/react-generator/test/generator/mockData.js

[error] 38-55: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)


[error] 905-905: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)


[error] 1046-1047: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)


[error] 1122-1122: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)

packages/react-generator/test/generator/expected/appdemo01/src/views/createVm/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)

🪛 ESLint
packages/react-generator/src/generator/page.js

[error] 110-110: 'generateJSXNode' was used before it was defined.

(@typescript-eslint/no-use-before-define)


[error] 192-192: 'description' is defined but never used. Allowed unused args must match /^_/u.

(@typescript-eslint/no-unused-vars)


[error] 192-192: 'state' is defined but never used. Allowed unused args must match /^_/u.

(@typescript-eslint/no-unused-vars)


[error] 437-437: A require() style import is forbidden.

(@typescript-eslint/no-require-imports)

🔇 Additional comments (2)
packages/react-generator/src/generator/page.js (2)

57-94: LGTM! Well-structured state update logic

The generateUpdateLogic function properly handles both simple and nested state updates, with good support for different component types (checkbox vs regular inputs) and both class and functional components.


650-657: Add useTranslation hook conditionally

Good implementation of conditional import for the i18n hook based on actual usage in the JSX.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (7)
packages/react-generator/README.md (7)

3-3: Fix badge: use npm version badge instead of pnpm to ensure it renders correctly on Shields.io.

Apply:

-[![pnpm version](https://img.shields.io/pnpm/v/@opentiny/tiny-engine-dsl-react.svg)](https://www.npmjs.com/package/@opentiny/tiny-engine-dsl-react) +[![npm version](https://img.shields.io/npm/v/@opentiny/tiny-engine-dsl-react.svg)](https://www.npmjs.com/package/@opentiny/tiny-engine-dsl-react)

90-110: State update for deep paths is correct; consider guiding toward useReducer for complex nested state.
Optional doc tip to avoid repetitive spread code for deeply nested structures.


165-181: Separate Prettier options from plugin options in the snippet for clarity.
Minor doc tweak: group “enableFormat/skipFiles/onlyFiles/logFormatResult” under a nested plugin key to avoid confusing them with Prettier core options.


304-324: Add language to fenced code block (markdownlint MD040).

-``` +```text src/ ├── generator/ # 代码生成器核心 │ ├── codeGenerator.js │ ├── generateApp.js │ ├── generateJsx.js │ ├── page.js │ └── parseImport.js ├── plugins/ # 插件系统 │ ├── formatCodePlugin.js │ ├── genBlockPlugin.js │ ├── genPagePlugin.js │ └── ... ├── templates/ # 代码模板 │ └── react-template/ ├── parser/ # DSL解析器 ├── pre-processor/ # 预处理器 ├── utils/ # 工具函数 └── constant/ # 常量定义
 --- `329-334`: **Add language to ASCII diagram code block (markdownlint MD040).** ```diff -``` +```text ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Plugin │ │ Template │ │ Parser │ │ Generator │ │ 插件系统 │───▶│ 模板系统 │───▶│ 解析器 │───▶│ 代码生成器 │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ 
 --- `6-18`: **Add a brief English summary or links for non-Chinese readers.** Optional, but improves discoverability for the public package. --- `19-23`: **Add usage note about package status and changelog.** Since this is a new public package, add a line linking to CHANGELOG for any future breaking changes (per project convention). I used the team learning that breaking changes are documented in the changelog for this repository. </blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **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. <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between f3d307ffc84174c13af4f5c6ce0d27e928a7cf44 and d9da61281466ea79f98d4a31513c6b4a144391a9. </details> <details> <summary>📒 Files selected for processing (1)</summary> * `packages/react-generator/README.md` (1 hunks) </details> <details> <summary>🧰 Additional context used</summary> <details> <summary>🧠 Learnings (1)</summary> <details> <summary>📚 Learning: 2025-01-14T08:42:18.574Z</summary> 

Learnt from: gene9831
PR: #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-generator/README.md` </details> </details><details> <summary>🪛 markdownlint-cli2 (0.17.2)</summary> <details> <summary>packages/react-generator/README.md</summary> 304-304: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 329-329: Fenced code blocks should have a language specified (MD040, fenced-code-language) </details> </details> </details> <details> <summary>🔇 Additional comments (5)</summary><blockquote> <details> <summary>packages/react-generator/README.md (5)</summary><blockquote> `67-86`: **Clarify event shape in data-binding example.** TinyInput.onChange may emit value (val) vs event (e). Document which one is expected to prevent incorrect handlers. --- `127-156`: **Docs show class components; confirm generator output style.** If the generator primarily emits function components with hooks, align the example; otherwise, state that lifecycle mapping uses class components (e.g., for componentDidCatch). --- `202-213`: **Align built-in plugin names with actual files/exports.** The list includes parseSchema, router, etc. Ensure names match the implemented plugin identifiers and filenames to prevent config drift. --- `232-252`: **List all public methods/exports exactly as shipped.** Verify that generate, generatePage, and generateBlock are the methods exposed by generateApp; document any additional exports (if any) to avoid API ambiguity. --- `281-292`: **Test scripts validated.** `test:latest` is defined in package.json and all referenced paths (`test/data-binding`, `test/lifecycle`, `test/formatCode`) exist under packages/react-generator. </blockquote></details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status --> 
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (1)
packages/react-generator/README.md (1)

115-121: Remove deprecated lifecycle from docs; add guidance.

componentWillMount is long-deprecated; documenting it without caveats encourages obsolete patterns. Remove it and add a short note discouraging use, with hook-based alternatives.

 #### 支持的生命周期 - `componentDidMount` - 组件挂载后 - `componentWillUnmount` - 组件卸载前 - `componentDidUpdate` - 组件更新后 - `componentDidCatch` - 错误边界捕获 -- `componentWillMount` - 组件挂载前 - `shouldComponentUpdate` - 控制组件更新 + +> 注:不建议使用已弃用的 `componentWillMount/UNSAFE_componentWillMount`。推荐使用 `useEffect(() => { /* init */ }, [])`, +> 或将初始化逻辑放置于构造/初始化阶段(函数组件的初始化或类组件构造函数)。
🧹 Nitpick comments (6)
packages/react-generator/README.md (6)

298-317: Fix markdownlint MD040: add language to fenced code block.

Label the project-structure block as plain text to satisfy linters and improve rendering.

-``` +```text src/ ├── generator/ # 代码生成器核心 │ ├── codeGenerator.js │ ├── generateApp.js │ ├── generateJsx.js │ ├── page.js │ └── parseImport.js ├── plugins/ # 插件系统 │ ├── formatCodePlugin.js │ ├── genBlockPlugin.js │ ├── genPagePlugin.js │ └── ... ├── templates/ # 代码模板 │ └── react-template/ ├── parser/ # DSL解析器 ├── pre-processor/ # 预处理器 ├── utils/ # 工具函数 └── constant/ # 常量定义
 --- `323-328`: **Fix markdownlint MD040: add language to ASCII diagram code block.** Mark as text for consistent formatting. ```diff -``` +```text ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Plugin │ │ Template │ │ Parser │ │ Generator │ │ 插件系统 │───▶│ 模板系统 │───▶│ 解析器 │───▶│ 代码生成器 │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ 
 --- `7-9`: **Clarify “双向绑定” phrasing to avoid React semantics confusion.** In React it’s controlled components (one-way data flow) producing a two-way UX. Slight wording tweak prevents misunderstandings. ```diff -- 🔄 **数据双向绑定** - 支持JSDataBinding和JSExpression的双向数据绑定 +- 🔄 **数据绑定(受控组件)** - 通过受控组件模式支持 JSDataBinding 与带 `model` 语义的 JSExpression,实现双向交互效果 

139-153: Prefer hook-based lifecycle example to match modern React.

You show class lifecycles here but earlier sections use hooks/state. Add a hooks equivalence example so readers adopt current patterns.

 class MyComponent extends React.Component { @@ } + +// Hooks 等价写法(推荐) +function MyComponent() { + React.useEffect(() => { + console.log('Component mounted') + return () => console.log('Component unmounted') + }, []) + return <div>Hello World</div> +}

26-35: Document return type/shape of generator.generate(schema).

Examples log/assign the result but don’t state whether it’s a文件映射、字符串、或包含入口信息的对象。 Please specify the exact return contract (and TypeScript类型), plus a tiny sample of the structure.

Would you confirm the public shape (e.g., { files: Record<string,string>, entry: string } or similar) and align README with src/index.d.ts?

Also applies to: 52-54


214-226: Clarify plugin lifecycle and registration points.

The example shows a transform plugin. Document all supported stages (transformStart/transform/transformEnd), execution order, and how multiple plugins compose/are ordered to reduce ambiguity.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9da612 and 67b73d8.

📒 Files selected for processing (1)
  • packages/react-generator/README.md (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
packages/react-generator/README.md

298-298: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


323-323: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (1)
packages/react-generator/README.md (1)

299-317: Verified: README paths exist and exports match.

All listed files are present under packages/react-generator/src/generator and exports are confirmed: default CodeGenerator in codeGenerator.js; generateApp exported from generateApp.js (and re-exported in generator/index.js); generateReactFile default-exported in generateJsx.js; parseImport (and genCompImport/getImportMap) exported in parseImport.js. No further changes required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (1)
packages/react-generator/README.md (1)

172-184: Remove/flag deprecated lifecycle; add guidance.

componentWillMount is deprecated/UNSAFE. Remove from “支持的生命周期” and add a cautionary note.

 #### 支持的生命周期 - `componentDidMount` - 组件挂载后 - `componentWillUnmount` - 组件卸载前 - `componentDidUpdate` - 组件更新后 - `componentDidCatch` - 错误边界捕获 -- `componentWillMount` - 组件挂载前 - `shouldComponentUpdate` - 控制组件更新 + +> 注:不建议使用已弃用的 `componentWillMount/UNSAFE_componentWillMount`。请将初始化逻辑置于构造/初始化阶段,或使用 `useEffect(() => {...}, [])`。
🧹 Nitpick comments (8)
packages/react-generator/README.md (8)

20-25: Fix MD040: add language to fenced block; optional Mermaid upgrade.

Add a language to satisfy markdownlint; consider Mermaid for readability.

-``` +```txt ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Plugin │ │ Template │ │ Parser │ │ Generator │ │ 插件系统 │───▶│ 模板系统 │───▶│ 解析器 │───▶│ 代码生成器 │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
 Optional: ```mermaid flowchart LR A[Plugin 插件系统] --> B[Template 模板系统] B --> C[Parser 解析器] C --> D[Generator 代码生成器] 

29-48: Fix MD040 on project-structure block.

Specify a language (use txt).

-``` +```txt src/ ├── generator/ # 代码生成器核心 ...
 --- `127-146`: **Split DSL and React snippets; use correct languages (json/jsx).** Current single javascript block mixes JSON DSL and React code. Split and label for clarity and linting. ```diff -```javascript -// DSL Schema -{ +```json +{ "componentName": "TinyInput", "props": { "value": { "type": "JSDataBinding", "value": "this.state.username" } } } - -// 生成的React代码 -const [state, setState] = useState({ username: '' }) - -<TinyInput - value={state.username} - onChange={e => setState(prev => ({ ...prev, username: e.target.value }))} -/> +``` + +```jsx +const [state, setState] = useState({ username: '' }) + +<TinyInput + value={state.username} + onChange={e => setState(prev => ({ ...prev, username: e.target.value }))} +/> 
 --- `151-170`: **Do the same split for the nested-binding example.** Label DSL as json and generated code as jsx. ```diff -```javascript -// DSL Schema -{ +```json +{ "componentName": "TinyInput", "props": { "value": { "type": "JSDataBinding", "value": "this.state.userInfo.name" } } } - -// 生成的React代码 -<TinyInput - value={state.userInfo.name} - onChange={e => setState(prev => ({ - ...prev, - userInfo: { ...prev.userInfo, name: e.target.value } - }))} -/> +``` + +```jsx +<TinyInput + value={state.userInfo.name} + onChange={e => setState(prev => ({ + ...prev, + userInfo: { ...prev.userInfo, name: e.target.value } + }))} +/> 
 --- `225-242`: **Separate Prettier options from plugin options to avoid confusion.** Document which keys are forwarded to Prettier vs. plugin-only. Suggest a nested `prettierOptions`. ```diff const generator = generateApp({ pluginConfig: { formatCode: { - // Prettier 配置 - printWidth: 100, - tabWidth: 2, - semi: true, - singleQuote: true, - - // 插件配置 - enableFormat: true, - skipFiles: ['txt', 'log'], - onlyFiles: ['js', 'jsx', 'ts', 'tsx'], - logFormatResult: true + enableFormat: true, + skipFiles: ['txt', 'log'], + onlyFiles: ['js', 'jsx', 'ts', 'tsx'], + logFormatResult: true, + // Prettier 配置(仅此对象会传递给 Prettier) + prettierOptions: { + printWidth: 100, + tabWidth: 2, + semi: true, + singleQuote: true + } } } }) 

50-56: Add environment prerequisites.

Document required Node/PNPM versions and workspace usage to reduce setup friction.

 ## 开发环境 + +建议环境 +- Node.js >= 18 +- pnpm >= 8 +- 在 monorepo 中通过 `pnpm -F @opentiny/tiny-engine-dsl-react` 进行安装/测试

294-312: Document generate() output shape.

Specify what generate(schema) returns (文件树结构?字符串集合?写入磁盘与否?) 以便集成方对接。

Example addition:

  • 返回值
    • files: 生成文件的 Map<{path:string, content:string}>
    • entry: 应用入口文件路径
    • warnings: string[]
    • write(outDir?: string): 将 files 落盘的便捷方法

285-289: Align customPlugins keys with hook phases.

Earlier示例与 CodeGenerator 钩子 transformStart/transform/transformEnd 不完全对应。建议在自定义插件示例中对齐并展示全部可用阶段。

 const generator = generateApp({ customPlugins: { - transform: [customPlugin] + transformStart: [], + transform: [customPlugin], + transformEnd: [] } })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67b73d8 and 722428d.

📒 Files selected for processing (3)
  • packages/react-generator/README.md (1 hunks)
  • packages/react-generator/src/templates/react-template/template-files/packageJson.js (1 hunks)
  • packages/react-generator/test/generator/expected/appdemo01/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react-generator/test/generator/expected/appdemo01/package.json
  • packages/react-generator/src/templates/react-template/template-files/packageJson.js
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
packages/react-generator/README.md

20-20: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


29-29: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (2)
packages/react-generator/README.md (2)

8-9: Use React‑accurate terminology (avoid “双向绑定”).

React is one‑way data flow with controlled components. Reword to prevent confusion.

[ suggest_nitpick_refactor ]

- - 🔄 **数据双向绑定** - 支持JSDataBinding和JSExpression的双向数据绑定 + - 🔄 **受控数据绑定** - 支持 JSDataBinding 与具备 model 语义的 JSExpression(基于受控组件与单向数据流)

187-216: Clarify emitted component style (class vs function).

Examples mix hooks and class components. State whether the生成器输出为函数组件、类组件,或按 DSL 决定,并注明对 componentDidCatch 需 Error Boundary(函数组件无等价生命周期)。

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (8)
packages/react-generator/test/generator/mockData.js (3)

38-58: Fix duplicate options key under getAllList.

The second options entry overwrites the array defined earlier, so the mock loses its list of option rows. Rename the request config key (e.g. to request) to keep both structures.

 ], - options: { + request: { uri: '', method: 'GET' },

896-902: Remove conflicting duplicate layout props.

Each Form sets layout: 'top' and then immediately overrides it with layout: false, leaving the mock in an invalid state and tripping lint. Drop the unintended duplicates so the intended layout survives.

- layout: 'top', - layout: false, + layout: 'top',

Apply the same fix to the other Form instances in this file.

Also applies to: 1038-1044, 1113-1121


334-339: Replace token-like literals with safe placeholders.

The confirmationToken values still look like real tokens and trigger secret scanners. Swap them for obvious placeholders so test data stays non-sensitive.

- confirmationToken: 'dfb2c162-351f-4f44-ad5f-8998', + confirmationToken: 'CONFIRMATION_TOKEN_PLACEHOLDER',

Repeat for the other occurrence in this file.

Also applies to: 1446-1450

packages/react-generator/src/constant/index.js (4)

21-378: Unify the Ant Design version across component maps.

DEFAULT_COMPONENTS_MAP pins AntD to ^3.10.0 while AntdComponents uses ^5.16.0. Mixing major versions under the same package name guarantees dependency conflicts and incompatible APIs in the generated app. Please introduce a single ANT_DESIGN_VERSION constant (targeting the actual version you support) and reuse it for every AntD mapping so the generated package.json stays coherent.


395-398: Restore the Tiny icon prefix.

Leaving TINY_ICON empty prevents the icon normalizer from ever detecting Tiny icons and also causes componentName.replace(TINY_ICON, 'icon') to inject 'icon' at the start of every name. Set the prefix (e.g. 'Tiny') so icon detection and import rewriting work as intended.


153-249: Fix incorrect Ant Design component mappings before shipping.

Several mappings point to APIs that Ant Design does not expose:

  • TinyTextarea tries to import a top-level TextArea; the real export is Input.TextArea.
  • TinyNumeric targets Numeric; AntD uses InputNumber.
  • TinyPager maps to Pager; the correct component is Pagination.
  • TinyPopeditor has no AntD equivalent; leaving it here generates a dead import.
  • TinySearch should resolve to Input.Search.
  • TinyTabItem and TinyTimeLine refer to non-existent exports (TabItem, TimeLine).

Every one of these will cause the generated project to fail at build time. Please realign them with existing components (using subName where needed) or drop unsupported entries entirely.

 { componentName: 'TinyTextarea', - exportName: 'TextArea', + exportName: 'Input', + subName: 'TextArea', package: 'antd', version: '^3.10.0', destructuring: true }, { componentName: 'TinyNumeric', - exportName: 'Numeric', + exportName: 'InputNumber', package: 'antd', version: '^3.10.0', destructuring: true }, { componentName: 'TinyPager', - exportName: 'Pager', + exportName: 'Pagination', package: 'antd', version: '^3.10.0', destructuring: true }, - { - componentName: 'TinyPopeditor', - exportName: 'Popeditor', - package: 'antd', - version: '^3.10.0', - destructuring: true - }, { componentName: 'TinySearch', - exportName: 'Search', + exportName: 'Input', + subName: 'Search', package: 'antd', version: '^3.10.0', destructuring: true }, { componentName: 'TinyTabItem', - exportName: 'TabItem', + exportName: 'Tabs', + subName: 'TabPane', package: 'antd', version: '^3.10.0', destructuring: true }, { - componentName: 'TinyTimeLine', - exportName: 'TimeLine', + componentName: 'TinyTimeline', + exportName: 'Timeline', package: 'antd', version: '^3.10.0', destructuring: true },

For TinyPopeditor, either provide a concrete mapping (e.g. to Popover) or remove it to avoid emitting broken imports.


33-94: Replace dotted AntD exports with subName mapping.

Entries such as TinyBreadcrumbItem, TinyButtonGroup, TinyCheckboxButton, TinyCheckboxGroup, and TinyCollapseItem set exportName to a dotted identifier (e.g. 'Breadcrumb.Item'). The generator later emits imports like import { Breadcrumb.Item as TinyBreadcrumbItem } from 'antd', which is a syntax error and will crash bundlers. Define the main export via exportName and move the dotted segment into subName so that we import Breadcrumb once and render <Breadcrumb.Item /> at runtime.

 { componentName: 'TinyBreadcrumbItem', - exportName: 'Breadcrumb.Item', + exportName: 'Breadcrumb', + subName: 'Item', package: 'antd', version: '^3.10.0', destructuring: true }, { componentName: 'TinyButtonGroup', - exportName: 'Button.Group', + exportName: 'Button', + subName: 'Group', package: 'antd', version: '^3.10.0', destructuring: true },

Apply the same pattern to the other dotted exports in this block so imports stay valid.

packages/react-generator/src/generator/page.js (1)

106-109: Spread event args when invoking handlers.

handleJSXEventBinding still calls ${eventHandler}(eventArgs, ...), so the original handler receives an array instead of its expected arguments (event, value, etc.). That breaks every generated onChange/onClick wrapper. Spread the rest parameters before appending extra params.

- const extendParams = item.params.join(',') - return `${key}={(...eventArgs) => ${eventHandler}(eventArgs, ${extendParams})}` + const extendParams = item.params.join(',') + const extra = extendParams ? `, ${extendParams}` : '' + return `${key}={(...eventArgs) => ${eventHandler}(...eventArgs${extra})}`
🧹 Nitpick comments (1)
packages/react-generator/src/utils/upperCase.js (1)

1-4: Filename corrected; rename parameter and consider internationalizing comments.

Good job fixing the filename from uaperCase.js to upperCase.js—this addresses the previous review feedback. However, a few improvements remain:

  • The parameter name string shadows JavaScript's global String constructor, which can confuse linters and readers. Rename it to str or text.
  • The inline comment is in Chinese; for consistency in an international codebase, use English (e.g., // Return early if falsy).
  • Optionally, add a typeof check to guard against non-string inputs being passed through.

Apply this diff:

-export const capitalizeFirstLetter = (string) => { - if (!string) return string // 检查空字符串 - return string.charAt(0).toUpperCase() + string.slice(1) +export const capitalizeFirstLetter = (str) => { + if (!str) return str // Return early if falsy + if (typeof str !== 'string') return str // Type guard (optional) + return str.charAt(0).toUpperCase() + str.slice(1) }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 722428d and 473de35.

📒 Files selected for processing (14)
  • packages/react-generator/README.md (1 hunks)
  • packages/react-generator/package.json (1 hunks)
  • packages/react-generator/src/constant/index.js (1 hunks)
  • packages/react-generator/src/generator/generateJsx.js (1 hunks)
  • packages/react-generator/src/generator/page.js (1 hunks)
  • packages/react-generator/src/generator/parseImport.js (1 hunks)
  • packages/react-generator/src/plugins/genRouterPlugin.js (1 hunks)
  • packages/react-generator/src/utils/upperCase.js (1 hunks)
  • packages/react-generator/test/data-binding/case1_data-binding/input/page.schema.json (1 hunks)
  • packages/react-generator/test/data-binding/case2_complex-binding/input/page.schema.json (1 hunks)
  • packages/react-generator/test/full/case1_normal/input/page.schema.json (1 hunks)
  • packages/react-generator/test/generator/mockData.js (1 hunks)
  • packages/react-generator/test/lifecycle/mockData.js (1 hunks)
  • packages/react-generator/vite.config.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/react-generator/package.json
  • packages/react-generator/test/lifecycle/mockData.js
  • packages/react-generator/vite.config.mjs
  • packages/react-generator/src/plugins/genRouterPlugin.js
  • packages/react-generator/src/generator/parseImport.js
  • packages/react-generator/README.md
  • packages/react-generator/test/data-binding/case2_complex-binding/input/page.schema.json
  • packages/react-generator/test/data-binding/case1_data-binding/input/page.schema.json
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: gene9831 PR: opentiny/tiny-engine#1041 File: packages/plugins/datasource/src/DataSourceList.vue:138-138 Timestamp: 2025-01-14T10:06:25.508Z Learning: PR #1041 in opentiny/tiny-engine is specifically for reverting Prettier v3 formatting to v2, without any logical code changes or syntax improvements. 
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. 
📚 Learning: 2025-01-13T03:46:13.817Z
Learnt from: yy-wow PR: opentiny/tiny-engine#940 File: packages/canvas/DesignCanvas/src/importMap.js:51-51 Timestamp: 2025-01-13T03:46:13.817Z Learning: The `getImportMapData` function in `packages/canvas/DesignCanvas/src/importMap.js` has default parameter handling that initializes `canvasDeps` with empty arrays for `scripts` and `styles`, making additional null checks unnecessary. 

Applied to files:

  • packages/react-generator/src/generator/page.js
📚 Learning: 2025-07-03T09:22:59.512Z
Learnt from: hexqi PR: opentiny/tiny-engine#1501 File: mockServer/src/tool/Common.js:79-82 Timestamp: 2025-07-03T09:22:59.512Z Learning: In the tiny-engine project, the mockServer code uses ES6 import syntax but is compiled to CommonJS output. This means CommonJS globals like `__dirname` are available at runtime, while ES6 module-specific features like `import.meta` would cause runtime errors. 

Applied to files:

  • packages/react-generator/src/generator/page.js
📚 Learning: 2024-09-25T11:18:00.771Z
Learnt from: chilingling PR: opentiny/tiny-engine#817 File: packages/vue-generator/src/plugins/appendElePlusStylePlugin.js:46-50 Timestamp: 2024-09-25T11:18:00.771Z Learning: In `appendElePlusStylePlugin.js`, the code uses `|| {}` to set default values when obtaining files, so additional null checks may not be necessary. 

Applied to files:

  • packages/react-generator/src/generator/generateJsx.js
📚 Learning: 2025-01-14T06:55:14.457Z
Learnt from: rhlin PR: opentiny/tiny-engine#1011 File: packages/canvas/render/src/canvas-function/design-mode.ts:6-13 Timestamp: 2025-01-14T06:55:14.457Z Learning: The code in `packages/canvas/render/src/canvas-function/design-mode.ts` is migrated code that should be preserved in its current form during the migration process. Refactoring suggestions for type safety and state management improvements should be considered in future PRs. 

Applied to files:

  • packages/react-generator/src/generator/generateJsx.js
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831 PR: opentiny/tiny-engine#1011 File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73 Timestamp: 2025-01-14T06:49:00.797Z Learning: In the tiny-engine project, the SvgIcon component is globally registered using `app.component('SvgIcon', SvgIcon)` in `packages/svgs/index.js`, making it available throughout Vue components without requiring explicit imports. 

Applied to files:

  • packages/react-generator/src/constant/index.js
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831 PR: opentiny/tiny-engine#1011 File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73 Timestamp: 2025-01-14T06:49:00.797Z Learning: In the tiny-engine project, the SvgIcon component is globally registered and available throughout Vue components without requiring explicit imports. 

Applied to files:

  • packages/react-generator/src/constant/index.js
🧬 Code graph analysis (3)
packages/react-generator/src/generator/page.js (6)
packages/react-generator/src/constant/index.js (5)
  • BUILTIN_COMPONENT_NAME (385-393)
  • IntrinsicElements (408-587)
  • AntdComponents (291-380)
  • TINY_ICON (398-398)
  • BUILTIN_COMPONENTS_MAP (267-289)
packages/react-generator/src/pre-processor/index.js (7)
  • schema (16-16)
  • schema (31-31)
  • schema (44-44)
  • schema (54-54)
  • schema (64-64)
  • schema (79-79)
  • preProcess (78-92)
packages/react-generator/src/utils/index.js (12)
  • description (128-128)
  • avoidDuplicateString (55-65)
  • isOn (92-92)
  • result (26-26)
  • result (48-48)
  • result (56-56)
  • toPascalCase (75-75)
  • exportName (131-131)
  • addIconRecord (127-136)
  • addAccessorRecord (109-117)
  • getFunctionInfo (24-39)
  • prettierOpts (83-87)
packages/react-generator/src/parser/state.js (3)
  • unwrapExpression (34-37)
  • translateHookState (43-57)
  • map (44-44)
packages/react-generator/src/utils/upperCase.js (2)
  • capitalizeFirstLetter (1-4)
  • capitalizeFirstLetter (1-4)
packages/react-generator/src/utils/formatCode.js (1)
  • formatCode (113-136)
packages/react-generator/src/generator/generateJsx.js (1)
packages/react-generator/src/generator/parseImport.js (7)
  • config (45-45)
  • config (70-70)
  • getImportMap (29-65)
  • getImportMap (29-65)
  • pkgMap (31-31)
  • pkgMap (69-69)
  • blockPkgMap (46-46)
packages/react-generator/src/constant/index.js (2)
packages/react-generator/src/parser/state-type.js (1)
  • UNWRAP_QUOTES (16-16)
packages/react-generator/src/parser/state.js (1)
  • UNWRAP_QUOTES (32-32)
🪛 Biome (2.1.2)
packages/react-generator/test/generator/mockData.js

[error] 38-55: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)


[error] 913-913: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)


[error] 1057-1058: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)


[error] 1135-1135: This property is later overwritten by an object member with the same name.

Overwritten with this property.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property.

(lint/suspicious/noDuplicateObjectKeys)

🔇 Additional comments (2)
packages/react-generator/test/full/case1_normal/input/page.schema.json (2)

1-8: Schema structure is valid and comprehensive for test coverage.

The JSON structure is well-formed and includes representative edge cases (i18n bindings, JSExpression/JSSlot, inline forms, dynamic grids, collections, computed properties) appropriate for validating the React generator pipeline. The state and methods sections provide realistic test data.

After the two critical/major issues above are resolved (component→componentName aliasing and quote escaping), this test input should exercise the full generator pipeline.


575-575: Change component field to componentName at lines 575 and 605 for generator compatibility.

Lines 575 and 605 use the component field, but the generator pipeline only recognizes componentName. These nodes will be silently skipped by parseImport and parseRequiredBlocks, causing the slot content to be dropped from generated output.

Change both occurrences:

 { - "component": "div", + "componentName": "div", "props": { ... } }
 { - "component": "span", + "componentName": "span", ... }

Line 616 already correctly uses componentName and requires no change.

Likely an incorrect or invalid review comment.

@hexqi hexqi merged commit 8b60a9f into opentiny:ospp-2025/source-to-dsl Oct 31, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request ospp ospp

3 participants