Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughAdds a new Upload toolbar (UI, metadata, registry integration) and a new Vue-to-DSL package with parsers, generator, CLI, tests, and app-level conversion features; integrates the toolbar into design-core, layout, aliases, and TypeScript paths. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UploadUI as Upload Toolbar (Main.vue)
participant FileInput as File Handler
participant Converter as VueToDslConverter
participant MetaAPI as Backend API (Pages)
participant Designer as App State / Canvas
User->>UploadUI: open upload menu / choose option
UploadUI->>FileInput: trigger file/directory/zip input
FileInput->>Converter: convertFromFile / convertAppFromZip
activate Converter
Converter->>Converter: parse SFCs (template/script/style)
Converter->>Converter: generate DSL schema / app schema
Converter-->>FileInput: return ConvertResult (schema, deps)
deactivate Converter
FileInput->>MetaAPI: fetchPageList(appId)
MetaAPI-->>FileInput: existing pages list
alt duplicates detected
FileInput->>UploadUI: prompt OverwriteDialog
User->>UploadUI: select overwrite choices
end
FileInput->>MetaAPI: create/update pages
MetaAPI-->>FileInput: success
FileInput->>Designer: update canvas / switch to imported page
Designer-->>User: show success notification
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip CodeRabbit can suggest fixes for GitHub Check annotations.Configure the |
There was a problem hiding this comment.
Actionable comments posted: 14
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (27)
packages/vue-to-dsl/test/full/input/appdemo01/src/views/createVm.vue-437-442 (1)
437-442:⚠️ Potential issue | 🟡 MinorScoped styles targeting
bodywill have no effect.The
<style scoped>directive only applies styles to elements within this component's template. Thebodyelement is outside the component scope, so these styles will not be applied.Consider removing or using unscoped style
If these styles are needed globally, either:
- Remove
scopedattribute, or- Move body styles to a global stylesheet, or
- Apply styles to the component's root
<div>instead-<style scoped> -body { - background-color: `#eef0f5`; - margin-bottom: 80px; -} +<style scoped> +/* Component-specific styles if needed */ </style>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/views/createVm.vue` around lines 437 - 442, The scoped <style> block in createVm.vue targets the global body element which is outside the component scope, so the body rules won’t apply; fix by either removing the scoped attribute from the <style> tag to make the body rules global, moving the body styles into a global stylesheet, or change the selector to target the component root element (e.g., the top-level <div> in this component) so the styles take effect within the scoped CSS.packages/vue-to-dsl/test/full/input/appdemo01/src/views/createVm.vue-26-32 (1)
26-32:⚠️ Potential issue | 🟡 MinorDuplicate and conflicting form props.
The
<tiny-form>components specify both camelCase and kebab-case versions of the same props with different values:
labelWidth="80px"vslabel-width="150px"labelPosition="top"vslabel-position="left "(also has trailing space)Only one value will apply. This pattern repeats throughout the file (lines 83-89, 197-203, 255-261, 339-345).
Proposed fix (apply to all form instances)
<tiny-form - labelWidth="80px" - labelPosition="top" :inline="false" - label-position="left " - label-width="150px" + label-position="left" + label-width="150px" style="border-radius: 0px" >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/views/createVm.vue` around lines 26 - 32, The tiny-form components use duplicate/conflicting prop names (camelCase vs kebab-case) with different values — e.g., labelWidth="80px" vs label-width="150px" and labelPosition="top" vs label-position="left " (note trailing space) — causing one to be ignored; pick a single canonical prop format per component (prefer kebab-case in templates) and remove the duplicate camelCase attributes, normalize values (remove trailing spaces) and ensure consistency across all <tiny-form> instances (including the other occurrences of the same component) so each form only declares label-width and label-position with the intended values.packages/vue-to-dsl/test/full/input/appdemo01/src/views/createVm.vue-404-405 (1)
404-405:⚠️ Potential issue | 🟡 MinorRemove unnecessary imports of
definePropsanddefineEmits.In Vue 3's
<script setup>,definePropsanddefineEmitsare compiler macros that are automatically available without importing. These imports should be removed.Proposed fix
import * as vue from 'vue' -import { defineProps, defineEmits } from 'vue' import { I18nInjectionKey } from 'vue-i18n'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/views/createVm.vue` around lines 404 - 405, Remove the explicit import of the compiler macros: delete the named import "defineProps" and "defineEmits" from the import statement that currently reads "import { defineProps, defineEmits } from 'vue'" (keep the existing "import * as vue from 'vue'" if other vue symbols are used) so the script relies on the built-in <script setup> compiler macros defineProps and defineEmits instead of importing them.packages/vue-to-dsl/README.md-49-52 (1)
49-52:⚠️ Potential issue | 🟡 MinorFix invalid Vue API usage in quick-start snippet.
Line 50 uses
vue.reactive(...)butvueis not defined, and thereactivefunction is not imported. This sample won't run as written. Replace with a direct import and usage ofreactive:Proposed fix
-import { ref } from 'vue' -const state = vue.reactive({ +import { reactive } from 'vue' +const state = reactive({ title: 'Hello TinyEngine' })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/README.md` around lines 49 - 52, The quick-start snippet incorrectly calls vue.reactive without defining `vue` and doesn't import `reactive`; change the import to include reactive (e.g., import { ref, reactive } from 'vue') and update the state initialization to call `reactive(...)` directly (replace `vue.reactive` with `reactive` in the `state` assignment) so the example uses the correct Vue API; also remove unused imports if needed.packages/vue-to-dsl/cli.ts-56-62 (1)
56-62:⚠️ Potential issue | 🟡 MinorInvalid
--formatvalue is silently ignored.When an invalid format value is provided (not 'json' or 'js'), the code silently falls through without warning the user. Consider adding a warning or error message.
🔧 Suggested fix
case '--format': case '-f': if (value === 'json' || value === 'js') { format = value + } else if (value) { + console.warn(`⚠️ 无效的格式: ${value},将使用默认格式 json`) } i += 2 break🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/cli.ts` around lines 56 - 62, The --format handling silently ignores invalid values: in the switch case for '--format' / '-f' where 'value' is checked and 'format' is set, add explicit validation that if value is not 'json' or 'js' you emit a clear error/warning (e.g., console.error or process.stderr.write) and exit with non-zero status or fallback to a known default; update the branch that currently just advances 'i' so that invalid values produce the message and terminate (or set default and inform user) rather than being ignored — reference the 'format' variable, the 'value' variable, and the '--format'/'-f' switch case to locate the change.packages/vue-to-dsl/test/testcases/002_createVM/expected/schema.json-117-132 (1)
117-132:⚠️ Potential issue | 🟡 MinorExpected schema contains propagated data bug:
[object Event].This schema correctly mirrors the input file, but it also captures the corrupted
background-color: [object Event]style value. Once the input file is fixed, this expected schema should also be updated.🔧 Fix needed after input is corrected
"componentName": "span", "props": { - "style": "background-color: [object Event]; color: `#8a8e99`; font-size: 12px" + "style": "color: `#8a8e99`; font-size: 12px" },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/testcases/002_createVM/expected/schema.json` around lines 117 - 132, The expected schema has a corrupted style value "background-color: [object Event]" for the span component (id "sfcukuue") — update the schema to match the corrected input by replacing the bad background-color token in the props.style for the span (componentName "span", id "sfcukuue") with the proper CSS value from the fixed source (or remove the background-color entry if the fixed input omits it), and ensure the expected file no longer captures event objects in style strings.packages/vue-to-dsl/test/testcases/002_createVM/input/component.vue-49-51 (1)
49-51:⚠️ Potential issue | 🟡 MinorInvalid style value:
[object Event]suggests serialization bug.The style attribute contains
background-color: [object Event];which is the string representation of an incorrectly serialized JavaScript Event object. This appears to be corrupted test data that should be corrected.🔧 Suggested fix
- <span style="background-color: [object Event]; color: `#8a8e99`; font-size: 12px" + <span style="color: `#8a8e99`; font-size: 12px"Or if a background color was intended:
- <span style="background-color: [object Event]; color: `#8a8e99`; font-size: 12px" + <span style="background-color: transparent; color: `#8a8e99`; font-size: 12px"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/testcases/002_createVM/input/component.vue` around lines 49 - 51, The span element with inline style containing "background-color: [object Event]" is corrupted by a serialized Event object; locate the span (the <span ...> 温馨提示:页面左上角切换区域 </span>) and either remove the invalid background-color entry from its style attribute or replace "[object Event]" with a valid CSS color value (e.g., a hex, rgb, or named color) or bind a proper reactive color variable instead; ensure the style string is a plain CSS value (not an Event) so the inline style becomes valid.packages/vue-to-dsl/src/parser/index.ts-43-46 (1)
43-46:⚠️ Potential issue | 🟡 MinorMissing error handling for file read failures.
parseVueFilecan throw if the file doesn't exist or is unreadable, but there's no try-catch or error propagation. Callers won't receive a consistent error structure likeparseSFCparsing errors.🛡️ Suggested error handling
export async function parseVueFile(filePath: string): Promise<any> { + try { const content = await fs.readFile(filePath, 'utf-8') return parseSFC(content) + } catch (error: any) { + console.warn(`Failed to read Vue file: ${filePath}`, error.message) + return { error: error.message } + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/parser/index.ts` around lines 43 - 46, parseVueFile currently reads files without handling read errors, so add a try-catch around the fs.readFile + parseSFC call in the parseVueFile function; on error, normalize and rethrow a consistent parsing error (matching parseSFC's error shape) that includes the original error message and context (e.g., filePath) so callers always receive a predictable error object. Ensure you reference parseVueFile and parseSFC when implementing the catch and rethrow logic.packages/vue-to-dsl/src/generator/index.ts-122-135 (1)
122-135:⚠️ Potential issue | 🟡 MinorDead code in
sanitizeSchemaStrings- array check after array return.Line 126 returns early for arrays, but line 128 still checks
Array.isArray(obj)in the ternary. This branch will never be reached for arrays, making[] :dead code.🔧 Fix dead code
function sanitizeSchemaStrings(obj: any): any { if (obj === null || obj === undefined) return obj if (typeof obj === 'string') return obj if (Array.isArray(obj)) return obj.map((v) => sanitizeSchemaStrings(v)) if (typeof obj === 'object') { - const out: any = Array.isArray(obj) ? [] : {} + const out: Record<string, any> = {} Object.keys(obj).forEach((k) => { out[k] = sanitizeSchemaStrings(obj[k]) }) return out } return obj }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/generator/index.ts` around lines 122 - 135, sanitizeSchemaStrings contains dead code: it returns early for arrays (Array.isArray(obj) => obj.map(...)) but later still uses Array.isArray(obj) when creating out, which is never reached for arrays. Fix by making the object branch consistent: either remove the early-array return and let the object branch handle arrays by setting const out: any = Array.isArray(obj) ? [] : {} and iterating keys, or keep the early-array return and change the object branch to always initialize out as {} (const out: any = {}), then recursively sanitize object properties. Update the function sanitizeSchemaStrings accordingly.packages/vue-to-dsl/src/generator/index.ts-168-179 (1)
168-179:⚠️ Potential issue | 🟡 Minor
generateAppSchemamutates inputpageSchemasarray.The function modifies
ps.meta.routerdirectly on the input objects (lines 174-176), which can cause unexpected side effects for callers who don't expect their input to be mutated.🛡️ Avoid mutation by working on copies
export function generateAppSchema(pageSchemas: any[], options: any = {}) { - // Ensure all pages have a router path without leading slash - if (pageSchemas && Array.isArray(pageSchemas)) { - for (const ps of pageSchemas) { - if (ps && ps.meta && ps.meta.router && typeof ps.meta.router === 'string') { - // Remove leading slash from router path - if (ps.meta.router.startsWith('/')) { - ps.meta.router = ps.meta.router.slice(1) - } - } - } - } + // Normalize router paths without mutating input + const normalizedPageSchemas = (pageSchemas || []).map((ps) => { + if (ps?.meta?.router && typeof ps.meta.router === 'string' && ps.meta.router.startsWith('/')) { + return { ...ps, meta: { ...ps.meta, router: ps.meta.router.slice(1) } } + } + return ps + }) return { // ... rest of return - pageSchema: pageSchemas || [], + pageSchema: normalizedPageSchemas,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/generator/index.ts` around lines 168 - 179, generateAppSchema currently mutates the caller's pageSchemas by assigning to ps.meta.router; instead, create and operate on a shallow/deep copy of the input (e.g., clone each page schema or map to a new array) before trimming leading slashes so original objects are not modified. Locate generateAppSchema and replace the in-place loop over pageSchemas with logic that builds a new array (or clones individual ps objects and their meta) and adjusts meta.router on the copies; ensure any return or further processing uses the new copied array rather than mutating the original pageSchemas.packages/vue-to-dsl/src/constants.ts-1-16 (1)
1-16:⚠️ Potential issue | 🟡 MinorAdd missing
TinyGridItementry todefaultComponentsMap.
defaultComponentMapincludes'tiny-grid-item': 'TinyGridItem'for template tag parsing, butdefaultComponentsMaplacks the corresponding component metadata. This will cause schema generation to fail whenTinyGridItemis used in templates, as the output won't include the necessary import information.Add the entry following the TinyGrid pattern
{ componentName: 'TinyGrid', package: '@opentiny/vue', exportName: 'Grid', destructuring: true, version: '3.24.0' }, + { componentName: 'TinyGridItem', package: '@opentiny/vue', exportName: 'GridItem', destructuring: true, version: '3.24.0' },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/constants.ts` around lines 1 - 16, defaultComponentsMap is missing the metadata entry for TinyGridItem which breaks schema generation when 'tiny-grid-item' is parsed; add a new entry for "TinyGridItem" to defaultComponentsMap using the same structure as the existing TinyGrid entry (same keys: importName/component name and import/source metadata used for TinyGrid) so that the tag 'tiny-grid-item' (mapped by defaultComponentMap) has matching component metadata for imports and schema generation.packages/vue-to-dsl/src/constants.ts-197-203 (1)
197-203:⚠️ Potential issue | 🟡 MinorUpdate TinyDropdown version to match other components.
TinyDropdown is versioned at
0.1.20, significantly older than all adjacent components in this file (which use3.24.0) and the current latest version (3.29.0). This appears to be a mistake rather than an intentional variant—update to3.24.0to align with the rest of the codebase.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/constants.ts` around lines 197 - 203, Update the TinyDropdown entry so its version matches the other components: find the object with componentName 'TinyDropdown' (package '@opentiny/vue', exportName 'Dropdown') and change its version field from '0.1.20' to '3.24.0' to align with adjacent component versions.packages/vue-to-dsl/src/index.ts-1-1 (1)
1-1:⚠️ Potential issue | 🟡 MinorRemove unnecessary
.d.tsimport.The
import './index.d.ts'statement serves no functional purpose. TypeScript declaration files are ambient declarations that are automatically available when the package is used; they don't need to be explicitly imported. Theindex.d.tsfile provides module augmentations for the package namespace, while the actual public API is provided by the re-exports on lines 3-7. This import pattern is unconventional and doesn't appear elsewhere in the codebase. Remove line 1 entirely.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/index.ts` at line 1, Remove the unnecessary explicit import of the declaration file by deleting the line `import './index.d.ts'` from the top of the module; keep the existing re-exports (the public API exports) intact (the subsequent export lines in this file) and run the TypeScript build/typecheck to confirm nothing else depends on that explicit import.packages/vue-to-dsl/test/full/input/appdemo01/src/views/LifeCyclePage.vue-30-31 (1)
30-31:⚠️ Potential issue | 🟡 MinorState missing properties used in template.
The template references
state.activeUsersandstate.systemLoad, but the reactive state is initialized as an empty object. If this fixture is meant to be runnable, add the missing properties.Proposed fix
-const state = vue.reactive({}) +const state = vue.reactive({ activeUsers: 0, systemLoad: 0 })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/views/LifeCyclePage.vue` around lines 30 - 31, The reactive state is initialized empty but the template expects state.activeUsers and state.systemLoad; update the vue.reactive call that constructs state (the symbol "state") to include those properties (e.g., provide sensible initial values for "activeUsers" and "systemLoad") so that wrap({ state }) receives a state object containing state.activeUsers and state.systemLoad used by the template.packages/vue-to-dsl/test/full/input/appdemo01/src/views/LifeCyclePage.vue-12-12 (1)
12-12:⚠️ Potential issue | 🟡 MinorTemplate references undefined method
refreshData.The button's
@clickhandler referencesrefreshData, but onlyhandleClickis defined in the script. This mismatch will cause a runtime error if the component is executed.Proposed fix: Either rename the method or the handler
- <tiny-button type="success" `@click`="refreshData">刷新数据</tiny-button> + <tiny-button type="success" `@click`="handleClick">刷新数据</tiny-button>Or rename the method:
-const handleClick = wrap(function handleClick() { +const refreshData = wrap(function refreshData() { console.log('Button clicked') }) -wrap({ handleClick }) +wrap({ refreshData })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/views/LifeCyclePage.vue` at line 12, The template uses a tiny-button with `@click`="refreshData" but the script only defines handleClick, causing a missing method runtime error; either rename the template handler to `@click`="handleClick" or add a refreshData method in the component that delegates to handleClick (e.g., create a refreshData method that calls this.handleClick) so the template and script method names match (referencing tiny-button, refreshData, and handleClick).packages/vue-to-dsl/test/full/input/appdemo01/src/http/index.js-19-24 (1)
19-24:⚠️ Potential issue | 🟡 MinorError interceptor swallows errors silently.
The error handler doesn't return a rejected promise or re-throw, causing all HTTP errors to be silently swallowed. Additionally,
error.responsemay be undefined for network errors, leading to a potential null reference on line 21.Proposed fix
- 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) + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/http/index.js` around lines 19 - 24, The response error interceptor (http.interceptors.response.use) currently assumes error.response exists and never re-throws or returns a rejected promise, which swallows errors and can NPE on network failures; fix by guarding access to error.response (e.g., check if error && error.response before reading status/headers), handle the 403/x-login-url flow inside that guard, and in all other cases (or after your login-handling flow if you don't retry) return Promise.reject(error) (or re-throw) so callers receive the error; reference the existing dataHandler and the anonymous error handler passed to http.interceptors.response.use to locate where to add these checks and the final Promise.reject(error).packages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/dataSource.js-36-38 (1)
36-38:⚠️ Potential issue | 🟡 MinorReturning
undefinedsilently may cause unexpected behavior.When
shouldFetch()returns false, the function returnsundefinedwithout any indication. Callers may not handle this case properly.Consider returning the current data or a specific value
if (!shouldFetch()) { - return + return dataSource.data // or return a specific sentinel value }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/dataSource.js` around lines 36 - 38, The early return when shouldFetch() is false silently yields undefined; update the function in dataSource.js that contains the shouldFetch() guard to return an explicit, documented value (e.g., the current cached data, null, or an empty array/Promise depending on the function's contract) instead of undefined so callers get a predictable result; adjust callers or JSDoc/type annotations if needed to reflect the chosen return value and ensure consistency with functions that consume this function.packages/vue-to-dsl/vite.config.js-36-40 (1)
36-40:⚠️ Potential issue | 🟡 MinorRemove unused and incorrectly configured alias.
The alias
'@': '/src'uses an absolute filesystem path that won't resolve correctly. However, since the@alias is not used anywhere in the source code, this dead configuration can be safely removed.If the alias is intended for use, replace it with a proper project-relative path:
Corrected alias configuration
+import { fileURLToPath, URL } from 'node:url' import { defineConfig } from 'vite' export default defineConfig({ // ... resolve: { alias: { - '@': '/src' + '@': fileURLToPath(new URL('./src', import.meta.url)) } } })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/vite.config.js` around lines 36 - 40, Remove the dead/incorrect alias entry in the Vite config: delete the '@': '/src' key from the resolve.alias object in vite.config.js since it uses an absolute filesystem path and is not used; if you actually need an '@' alias, set it to a proper project-relative path (e.g., use path.resolve(__dirname, 'src') or a relative './src') and ensure imports use '@' so the alias is necessary.packages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/lowcode.js-30-33 (1)
30-33:⚠️ Potential issue | 🟡 MinorPotential runtime error if
callbackis not provided.The
setStatefunction callscallback.apply(global)without checking ifcallbackis defined. If called without a callback argument, this will throw a TypeError.🛡️ Proposed defensive check
const setState = (newState, callback) => { Object.assign(global.state, newState) - nextTick(() => callback.apply(global)) + nextTick(() => callback?.apply(global)) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/lowcode.js` around lines 30 - 33, The setState function currently calls callback.apply(global) unguarded which will throw if callback is undefined; update setState (referencing setState, global.state and nextTick) to defensively handle an optional callback by either checking typeof callback === 'function' before calling or defaulting callback to a no-op, and invoke it with the proper this (global) inside nextTick only when it's a function.packages/vue-to-dsl/test/full/input/appdemo01/src/http/axios.js-37-39 (1)
37-39:⚠️ Potential issue | 🟡 MinorError handler assumes
error.responseexists.The catch block directly accesses
error.response.statusanderror.response.data. For network errors or timeouts,error.responsemay be undefined, causing a TypeError.🛡️ Proposed defensive error handling
.catch((error) => { - resolve([error.response.status, error.response.data]) + if (error.response) { + resolve([error.response.status, error.response.data]) + } else { + resolve([0, { message: error.message || 'Network Error' }]) + } })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/http/axios.js` around lines 37 - 39, The catch block in the axios wrapper currently assumes error.response exists and will throw if a network error or timeout occurs; update the catch handler (the .catch((error) => { ... }) in axios.js) to defensively read error.response using optional chaining or existence checks and fall back to sensible defaults (e.g., status 0 or null and data set to error.message or error.request info) before resolving so network/timeouts don't cause a TypeError. Ensure the resolved tuple uses error.response?.status and error.response?.data fallback values and include error.message when response is absent.packages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/dataSource.json-628-631 (1)
628-631:⚠️ Potential issue | 🟡 MinorTypo in function name:
dataHanldershould bedataHandler.The root-level dataHandler function (line 630) has a typo:
"function dataHanlder(res)"should be"function dataHandler(res)". If this is intentional test data to verify the converter handles misspellings, consider adding a comment indicating this is deliberate.✏️ Fix the typo
"dataHandler": { "type": "JSFunction", - "value": "function dataHanlder(res){\n return res;\n}" + "value": "function dataHandler(res){\n return res;\n}" }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/dataSource.json` around lines 628 - 631, The JSON object under the "dataHandler" property contains a JSFunction string with a misspelled function name ("function dataHanlder(res)"); update the function declaration inside the "value" string to use the correct name "function dataHandler(res)" so the exported function name matches the property key (or, if the misspelling is intentional for a test, add a clear comment in the test data indicating that it's deliberate); look for the "dataHandler" property and its "value" string to make this change.packages/vue-to-dsl/src/index.d.ts-58-62 (1)
58-62:⚠️ Potential issue | 🟡 MinorType mismatch:
ImportInfo.specifiersshould be an array of objects, not strings.In the implementation (
converter.tslines 75-76 andtemplateParser.tslines 134-152),specifiersis used as{ local: string, imported?: string }[], notstring[].🐛 Proposed fix
export interface ImportInfo { source: string - specifiers: string[] + specifiers: Array<{ local: string; imported?: string }> default?: string }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/index.d.ts` around lines 58 - 62, Update the ImportInfo interface so specifiers matches its actual usage: change specifiers from string[] to an array of objects with shape { local: string; imported?: string } (used by converter.ts and templateParser.ts), ensuring the declaration in index.d.ts reflects this composite type for ImportInfo so callers like the functions in converter.ts and templateParser.ts can access specifier.local and specifier.imported correctly.packages/vue-to-dsl/src/parsers/templateParser.ts-272-274 (1)
272-274:⚠️ Potential issue | 🟡 MinorFix invalid empty character class
[^]in regex patterns.The regex uses
[^]which is flagged as an empty negated character class. While some JS engines interpret this as "match any character," it's non-standard. Use[\s\S]for cross-platform compatibility and clarity.🐛 Proposed fix
const match = - exp.match(/^[^]*?(?:\)|\S)\s+(?:in|of)\s+([^]+)$/) || exp.match(/^(?:[^]+?)\s+(?:in|of)\s+([^]+)$/) + exp.match(/^[\s\S]*?(?:\)|\S)\s+(?:in|of)\s+([\s\S]+)$/) || exp.match(/^(?:[\s\S]+?)\s+(?:in|of)\s+([\s\S]+)$/)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/parsers/templateParser.ts` around lines 272 - 274, Replace the non-standard empty negated class `[^]` with the cross-platform `[\s\S]` in the regexes used to parse the iteration source in templateParser.ts (the expressions that produce `match` from `exp.match(...)`), so update both occurrences in the `match` assignment to use `[\s\S]` instead of `[^]`; keep the rest of the pattern and then keep `const src = (match ? match[1] : exp).trim()` as-is so the captured group behavior remains unchanged.packages/vue-to-dsl/src/converter.ts-116-119 (1)
116-119:⚠️ Potential issue | 🟡 MinorAvoid mutating
this.optionsduring conversion.Setting
this.options.fileNameduringconvertFromStringmakes the converter stateful and unsafe for concurrent conversions. Pass the fileName through the options parameter togenerateSchemainstead.🐛 Proposed fix
- // Set fileName in options for schema generation - if (fileName) { - this.options.fileName = fileName.replace(/\.vue$/i, '') - } - - const schema = await generateSchema(templateSchema, scriptSchema, styleSchema, this.options as any) + const schemaOptions = { + ...this.options, + fileName: fileName ? fileName.replace(/\.vue$/i, '') : this.options.fileName + } + const schema = await generateSchema(templateSchema, scriptSchema, styleSchema, schemaOptions as any)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/converter.ts` around lines 116 - 119, The converter currently mutates instance state by setting this.options.fileName inside convertFromString; instead, avoid changing this.options and pass the sanitized fileName through the local options when calling generateSchema. In convertFromString, compute a fileName value (e.g. strip the .vue suffix) and merge it into the options object you pass to generateSchema (rather than assigning to this.options.fileName) so convertFromString and generateSchema receive the fileName without making the converter stateful.packages/vue-to-dsl/src/parsers/templateParser.ts-278-279 (1)
278-279:⚠️ Potential issue | 🟡 Minor
v-showdirective value should useJSExpressionwrapper for consistency.Other directives like
v-model,v-on, andv-bindwrap their values in{ type: 'JSExpression', value: ... }, butv-showstores the raw expression string. This inconsistency may cause issues in downstream processing.🐛 Proposed fix
case 'show': - schema.props['v-show'] = prop.exp ? prop.exp.content : 'true' + schema.props['v-show'] = { + type: 'JSExpression', + value: ensureThisPrefix(prop.exp ? prop.exp.content : 'true', loopVariable) + } break🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/parsers/templateParser.ts` around lines 278 - 279, The v-show handling in templateParser.ts stores the raw expression string into schema.props['v-show'] instead of the consistent JSExpression wrapper used by v-model/v-on/v-bind; change the assignment in the case 'show' branch so schema.props['v-show'] is set to an object { type: 'JSExpression', value: prop.exp ? prop.exp.content : 'true' } (using the same prop.exp and fallback logic currently present) so downstream consumers receive a JSExpression for v-show just like other directives.packages/vue-to-dsl/src/parsers/templateParser.ts-10-16 (1)
10-16:⚠️ Potential issue | 🟡 MinorPotential ReDoS vulnerability with user-controlled regex input.
The
loopVariableis extracted from user-provided Vue template content. While the regex pattern itself is simple, building regexes from user input is a security concern. Consider sanitizing the loop variable or using a safer approach.🛡️ Proposed fix to escape special regex characters
+function escapeRegExp(str: string): string { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +} + function ensureThisPrefix(exp: string, loopVariable?: string) { const v = String(exp || '').trim() if (!v) return v if (v.startsWith('this.')) return v // If we're inside a loop and the expression starts with the loop variable, don't prefix if (loopVariable) { // Check if expression starts with the loop variable name - const loopVarPattern = new RegExp(`^${loopVariable}(?:\\.|\\[|$)`) + const loopVarPattern = new RegExp(`^${escapeRegExp(loopVariable)}(?:\\.|\\[|$)`) if (loopVarPattern.test(v)) { return v } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/parsers/templateParser.ts` around lines 10 - 16, The regex construction using loopVariable in templateParser.ts (loopVarPattern) can be abused; instead escape regex metacharacters in loopVariable before building loopVarPattern or avoid regex entirely by checking v.startsWith(loopVariable) and then ensuring the following character (if any) is '.' '[' or end-of-string; update the logic in the block that references loopVariable and loopVarPattern to use a safe escaped value (via a utility like escapeRegex) or a pure string-check approach so user-controlled template names cannot inject regex patterns.packages/vue-to-dsl/src/converter.ts-796-809 (1)
796-809:⚠️ Potential issue | 🟡 MinorPotential ReDoS vulnerability in gitignore pattern regex conversion.
The conversion of gitignore patterns to regex using
.*for**can cause catastrophic backtracking when malicious patterns are combined with specific input paths. Since gitignore content is loaded from user-uploaded files, this is exploitable. Consider using theignorenpm package, which safely implements the gitignore specification.🛡️ Suggested safer approach
+import ignore from 'ignore' + const createGitignoreFilter = (gitignoreContent: string) => { - const lines = gitignoreContent - .split('\n') - .map((l) => l.trim()) - .filter((l) => l && !l.startsWith('#')) - const patterns = lines.map((line) => { - // ... regex conversion - }) - return (path: string) => { ... } + const ig = ignore().add(gitignoreContent) + return (path: string) => !ig.ignores(path) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vue-to-dsl/src/converter.ts` around lines 796 - 809, The current conversion in converter.ts builds regexes by replacing gitignore tokens into strings (see the pattern -> regexString replacements and the returned RegExp using `.*` for `**`), which is vulnerable to ReDoS; replace this custom conversion by delegating gitignore parsing/matching to a safe, well-tested library (e.g., the `ignore` npm package) instead of building RegExp from `regexString` — update the code path that maps `pattern` to `{ regex, isNegative }` to use the `ignore` matcher API (or equivalent safe matcher) to produce match results, remove the manual `.replace()` conversions around `regexString`, and ensure directory handling (the branch checking `regexString.endsWith('/')`) is handled by the library's semantics.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 7783fb69-a1c0-4463-81fe-b359a8d9c2d0
⛔ Files ignored due to path filters (2)
packages/vue-to-dsl/test/full/input/appdemo01.zipis excluded by!**/*.zippackages/vue-to-dsl/test/full/input/appdemo01/public/favicon.icois excluded by!**/*.ico
📒 Files selected for processing (78)
packages/build/vite-config/src/vite-plugins/devAliasPlugin.jspackages/design-core/package.jsonpackages/design-core/re-export.jspackages/design-core/registry.jspackages/layout/src/defaultLayout.jspackages/register/src/constants.tspackages/toolbars/upload/index.tspackages/toolbars/upload/meta.jspackages/toolbars/upload/package.jsonpackages/toolbars/upload/src/Main.vuepackages/toolbars/upload/src/OverwriteDialog.vuepackages/toolbars/upload/src/http.tspackages/toolbars/upload/src/styles/vars.lesspackages/vue-to-dsl/.gitignorepackages/vue-to-dsl/README.mdpackages/vue-to-dsl/cli.tspackages/vue-to-dsl/package.jsonpackages/vue-to-dsl/src/constants.tspackages/vue-to-dsl/src/converter.tspackages/vue-to-dsl/src/generator/index.tspackages/vue-to-dsl/src/index.d.tspackages/vue-to-dsl/src/index.tspackages/vue-to-dsl/src/parser/index.tspackages/vue-to-dsl/src/parsers/index.tspackages/vue-to-dsl/src/parsers/scriptParser.tspackages/vue-to-dsl/src/parsers/styleParser.tspackages/vue-to-dsl/src/parsers/templateParser.tspackages/vue-to-dsl/src/shims.d.tspackages/vue-to-dsl/src/types.tspackages/vue-to-dsl/src/types/index.jspackages/vue-to-dsl/test/full/expected/schema.from-zip.jsonpackages/vue-to-dsl/test/full/expected/schema.jsonpackages/vue-to-dsl/test/full/index.test.jspackages/vue-to-dsl/test/full/input/appdemo01/.gitignorepackages/vue-to-dsl/test/full/input/appdemo01/README.mdpackages/vue-to-dsl/test/full/input/appdemo01/index.htmlpackages/vue-to-dsl/test/full/input/appdemo01/package.jsonpackages/vue-to-dsl/test/full/input/appdemo01/src/App.vuepackages/vue-to-dsl/test/full/input/appdemo01/src/http/axios.jspackages/vue-to-dsl/test/full/input/appdemo01/src/http/config.jspackages/vue-to-dsl/test/full/input/appdemo01/src/http/index.jspackages/vue-to-dsl/test/full/input/appdemo01/src/i18n/en_US.jsonpackages/vue-to-dsl/test/full/input/appdemo01/src/i18n/index.jspackages/vue-to-dsl/test/full/input/appdemo01/src/i18n/locale.jspackages/vue-to-dsl/test/full/input/appdemo01/src/i18n/zh_CN.jsonpackages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/bridge.jspackages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/dataSource.jspackages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/dataSource.jsonpackages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/lowcode.jspackages/vue-to-dsl/test/full/input/appdemo01/src/lowcodeConfig/store.jspackages/vue-to-dsl/test/full/input/appdemo01/src/main.jspackages/vue-to-dsl/test/full/input/appdemo01/src/router/index.jspackages/vue-to-dsl/test/full/input/appdemo01/src/stores/index.jspackages/vue-to-dsl/test/full/input/appdemo01/src/stores/testState.jspackages/vue-to-dsl/test/full/input/appdemo01/src/utils.jspackages/vue-to-dsl/test/full/input/appdemo01/src/views/DemoPage.vuepackages/vue-to-dsl/test/full/input/appdemo01/src/views/LifeCyclePage.vuepackages/vue-to-dsl/test/full/input/appdemo01/src/views/UntitledA.vuepackages/vue-to-dsl/test/full/input/appdemo01/src/views/createVm.vuepackages/vue-to-dsl/test/full/input/appdemo01/vite.config.jspackages/vue-to-dsl/test/sfc/converter.test.jspackages/vue-to-dsl/test/testcases/001_simple/expected/schema.jsonpackages/vue-to-dsl/test/testcases/001_simple/input/component.vuepackages/vue-to-dsl/test/testcases/002_createVM/expected/schema.jsonpackages/vue-to-dsl/test/testcases/002_createVM/input/component.vuepackages/vue-to-dsl/test/testcases/003_login/expected/schema.jsonpackages/vue-to-dsl/test/testcases/003_login/input/component.vuepackages/vue-to-dsl/test/testcases/004_dashboard/expected/schema.jsonpackages/vue-to-dsl/test/testcases/004_dashboard/input/component.vuepackages/vue-to-dsl/test/testcases/005_survey/expected/schema.jsonpackages/vue-to-dsl/test/testcases/005_survey/input/component.vuepackages/vue-to-dsl/test/testcases/006_lifecycle/expected/schema.jsonpackages/vue-to-dsl/test/testcases/006_lifecycle/input/component.vuepackages/vue-to-dsl/test/testcases/index.test.jspackages/vue-to-dsl/tsconfig.jsonpackages/vue-to-dsl/vite.config.cli.mjspackages/vue-to-dsl/vite.config.jstsconfig.app.json
| const processAppSchema = async (appSchema: any) => { | ||
| // 将 appSchema 应用到全局 | ||
| const { appSchemaState } = useResource() | ||
| // 1) 全局元数据(i18n/utils/dataSource/globalState/componentsMap) | ||
| const i18n = appSchema?.i18n || {} | ||
| const locales = Object.keys(i18n).length | ||
| ? Object.keys(i18n).map((key) => ({ lang: key, label: key })) | ||
| : [ | ||
| { lang: 'zh_CN', label: 'zh_CN' }, | ||
| { lang: 'en_US', label: 'en_US' } | ||
| ] | ||
| appSchemaState.langs = { | ||
| locales, | ||
| messages: i18n | ||
| } | ||
| appSchemaState.utils = appSchema?.utils || [] | ||
| appSchemaState.dataSource = appSchema?.dataSource?.list || [] | ||
| appSchemaState.globalState = appSchema?.globalState || [] | ||
| appSchemaState.componentsMap = appSchema?.componentsMap || appSchemaState.componentsMap | ||
|
|
||
| // 同步刷新 i18n 到画布/设计器 | ||
| const { id, type } = getMetaApi(META_SERVICE.GlobalService).getBaseInfo() | ||
| await useTranslate().initI18n({ host: id, hostType: type, init: true }) | ||
|
|
||
| // 2) 创建静态页面(批量)并刷新页面树 | ||
| const pages = Array.isArray(appSchema?.pageSchema) ? appSchema.pageSchema : [] | ||
| // 将应用级 schema 归一化并持久化,便于 Http 拦截器统一返回 | ||
| try { | ||
| const componentsTree = pages.map((ps: any) => ({ | ||
| name: ps?.meta?.name || ps?.fileName || 'Page', | ||
| meta: { ...(ps?.meta || {}), isPage: true }, | ||
| page_content: ps | ||
| })) | ||
| if (!componentsTree.some((p: any) => p?.meta?.isHome) && componentsTree[0]) { | ||
| componentsTree[0].meta.isHome = true | ||
| } | ||
| const normalizedAppData = { | ||
| ...appSchema, | ||
| componentsTree, | ||
| pageSchema: pages, | ||
| meta: { | ||
| ...(appSchema.meta || {}), | ||
| globalState: (appSchema.meta && appSchema.meta.globalState) || appSchema.globalState || [] | ||
| } | ||
| } | ||
| localStorage.setItem('TE_LOCAL_APPSCHEMA', JSON.stringify(normalizedAppData)) | ||
| } catch (e) { | ||
| // ignore persistence errors | ||
| } | ||
| const appId = getMetaApi(META_SERVICE.GlobalService).getBaseInfo().id | ||
| // 保存 appId 供覆盖选择确认/取消时使用 | ||
| state.appId = appId |
There was a problem hiding this comment.
Delay app-schema side effects until overwrite is confirmed.
Lines 125-170 commit appSchemaState, reinitialize i18n, and persist TE_LOCAL_APPSCHEMA before the duplicate-page branch at Lines 218-223 can still abort the import. If the user cancels the overwrite dialog, the UI says nothing was imported, but those global mutations have already been applied.
Also applies to: 218-223
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/toolbars/upload/src/Main.vue` around lines 124 - 175, The code
currently applies global side effects (mutating appSchemaState, calling
useTranslate().initI18n, writing TE_LOCAL_APPSCHEMA to localStorage, and setting
state.appId) inside processAppSchema before the overwrite confirmation may
abort; instead, make processAppSchema only produce/stage the normalizedAppData,
componentsTree, locales, and appId (e.g., return an object or set a transient
pendingAppSchema/pendingAppMeta field) and do NOT call useTranslate().initI18n,
mutate appSchemaState, write localStorage, or set state.appId there; then, in
the overwrite-confirmation handler (the duplicate-page/overwrite branch), when
the user confirms, apply the staged changes by assigning to appSchemaState,
calling useTranslate().initI18n, persisting TE_LOCAL_APPSCHEMA, and setting
state.appId; if the user cancels, discard the staged data so no global mutations
occur.
| try { | ||
| const existingList: any[] = await fetchPageList(appId) | ||
| const mapByName = new Map<string, any>() | ||
| existingList?.forEach?.((p: any) => { | ||
| if (p?.name) mapByName.set(String(p.name), p) | ||
| }) | ||
|
|
||
| const getRawName = (ps: any) => (ps?.meta?.name || ps?.fileName || 'Page') as string | ||
| const pagesToUpdate: Array<{ ps: any; existing: any; name: string }> = [] | ||
| const pagesToCreate: any[] = [] | ||
| const duplicateNames: string[] = [] | ||
|
|
||
| for (const ps of pages) { | ||
| const rawName = getRawName(ps) | ||
| const existing = mapByName.get(rawName) | ||
| if (existing) { | ||
| pagesToUpdate.push({ ps, existing, name: rawName }) | ||
| duplicateNames.push(rawName) | ||
| } else { | ||
| pagesToCreate.push(ps) | ||
| } | ||
| } | ||
|
|
||
| if (duplicateNames.length) { | ||
| // 打开覆盖选择对话框并缓存数据,等待用户选择 | ||
| state.duplicatePages = pagesToUpdate | ||
| state.toCreatePages = pagesToCreate | ||
| state.pendingImportedPages = pages | ||
| state.showOverwriteDialog = true | ||
| } else { | ||
| // 无重名,直接创建 | ||
| await Promise.allSettled( | ||
| pagesToCreate.map((ps: any) => | ||
| getMetaApi(META_SERVICE.Http).post('/app-center/api/pages/create', buildCreateParams(ps)) | ||
| ) | ||
| ) | ||
| const { pageSettingState } = usePage() | ||
| await pageSettingState.updateTreeData?.() | ||
| } | ||
| } catch (e) { | ||
| // 若校验失败,则回退为原始创建逻辑 | ||
| await Promise.allSettled( | ||
| pages.map((ps: any) => | ||
| getMetaApi(META_SERVICE.Http).post('/app-center/api/pages/create', buildCreateParams(ps)) | ||
| ) | ||
| ) | ||
| const { pageSettingState } = usePage() | ||
| await pageSettingState.updateTreeData?.() | ||
| } |
There was a problem hiding this comment.
Don't retry batch creates from this broad fallback.
This try wraps duplicate lookup, page creation, and tree refresh together. If updateTreeData() fails after some /pages/create calls already succeeded, the catch posts every page again, which retries a non-idempotent write and can duplicate data.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/toolbars/upload/src/Main.vue` around lines 195 - 243, The catch is
currently retrying all page creates on any error (including updateTreeData),
causing duplicate non-idempotent writes; refactor so that only the
duplicate-detection and create requests are inside the try and do NOT re-post
pages in the catch. Specifically, in the block around fetchPageList, mapByName,
pagesToUpdate/pagesToCreate and the Promise.allSettled that calls
getMetaApi(...).post('/app-center/api/pages/create', buildCreateParams(ps)),
remove the fallback that re-iterates pages in the catch; instead handle errors
by setting state (state.showOverwriteDialog/state.pendingImportedPages) or
logging the error and either rethrowing or returning early. Also isolate
pageSettingState.updateTreeData() into its own try/catch so failures to refresh
the tree do not trigger re-creating pages. Ensure references: fetchPageList,
buildCreateParams, getMetaApi(...).post('/app-center/api/pages/create', ...),
usePage()/pageSettingState.updateTreeData, and state. Do not change API call
semantics—only stop retrying creates from the broad catch and handle
updateTreeData errors separately.
| const rawName = (result?.schema?.meta?.name || file.name).replace(/\.(vue|jsx|tsx)$/i, '') | ||
| const safeRoute = `${rawName.replace(/\s+/g, '-').toLowerCase()}` |
There was a problem hiding this comment.
Normalize the single-file fallback route with a leading slash.
Line 268 builds foo-bar, while the app import path uses /${...}. Single-file imports without meta.router end up with a different route shape and can break routing/lookups that expect a leading slash.
Suggested fix
- const safeRoute = `${rawName.replace(/\s+/g, '-').toLowerCase()}`
+ const safeRoute = `/${rawName.replace(/\s+/g, '-').toLowerCase()}`Also applies to: 293-296, 312-315
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/toolbars/upload/src/Main.vue` around lines 267 - 268, The fallback
route normalization currently produces values like "foo-bar" (const rawName /
const safeRoute) whereas consumers expect a leading slash; update the safeRoute
construction in the single-file fallback branches (the places using rawName →
safeRoute around the rawName.replace(...) calls and the other similar blocks at
the other two occurrences) to prepend a slash, e.g. build the route as `"/" +
rawName.replace(/\s+/g, '-').toLowerCase()` (or template `/${...}`) so all
fallback routes consistently include a leading "/" before returning/using them.
| try { | ||
| const existingList: any[] = await fetchPageList(appId) | ||
| const existing = existingList?.find?.((p: any) => String(p?.name) === rawName) | ||
|
|
||
| // 将待导入的 schema 作为 ps(与 zip 流程保持一致的数据形态) | ||
| const ps: any = { | ||
| ...result.schema, | ||
| meta: { ...(result?.schema?.meta || {}), name: rawName }, | ||
| fileName | ||
| } | ||
|
|
||
| if (existing) { | ||
| // 重名:弹出覆盖对话框 | ||
| state.appId = appId | ||
| state.duplicatePages = [{ ps, existing, name: rawName }] | ||
| state.toCreatePages = [] | ||
| state.pendingImportedPages = [ps] | ||
| state.showOverwriteDialog = true | ||
| } else { | ||
| // 不重名:直接创建 | ||
| const createParams: any = { | ||
| name: rawName, | ||
| route: result?.schema?.meta?.router || safeRoute, | ||
| group: 'staticPages', | ||
| parentId: '0', | ||
| isPage: true, | ||
| app: appId, | ||
| page_content: { | ||
| ...result.schema, | ||
| fileName | ||
| } | ||
| } | ||
| await getMetaApi(META_SERVICE.Http).post('/app-center/api/pages/create', createParams) | ||
| const { pageSettingState } = usePage() | ||
| await pageSettingState.updateTreeData?.() | ||
| useNotify({ type: 'success', title: '导入成功', message: `已创建新页面:${rawName}` }) | ||
| } | ||
| } catch (e: any) { | ||
| // 兜底:如果列表获取失败,按原逻辑创建 | ||
| const createParams: any = { | ||
| name: rawName, | ||
| route: result?.schema?.meta?.router || safeRoute, | ||
| group: 'staticPages', | ||
| parentId: '0', | ||
| isPage: true, | ||
| app: appId, | ||
| page_content: { | ||
| ...result.schema, | ||
| fileName | ||
| } | ||
| } | ||
| await getMetaApi(META_SERVICE.Http).post('/app-center/api/pages/create', createParams) | ||
| const { pageSettingState } = usePage() | ||
| await pageSettingState.updateTreeData?.() | ||
| useNotify({ type: 'success', title: '导入成功', message: `已创建新页面:${rawName}` }) | ||
| } |
There was a problem hiding this comment.
Scope the single-file fallback to fetchPageList() only.
This catch retries /pages/create on any error in the block, not just page-list lookup failures. A successful create followed by an updateTreeData() failure will send the same create request a second time.
| if (requests.length) { | ||
| await Promise.allSettled(requests) | ||
| } | ||
| const hasOps = requests.length > 0 | ||
| if (hasOps) { | ||
| await pageSettingState.updateTreeData?.() | ||
| // 用户选择后进行渲染 | ||
| const pages = state.pendingImportedPages | ||
| const chosen = pages.find((p: any) => p?.meta?.isHome) || pages[0] | ||
| if (chosen) { | ||
| await switchToPageByName(chosen?.meta?.name || chosen?.fileName) | ||
| useNotify({ | ||
| type: 'success', | ||
| title: '导入成功', | ||
| message: `已创建/覆盖页面并加载:${chosen?.meta?.name || '页面'}` | ||
| }) | ||
| } else { | ||
| useNotify({ type: 'success', title: '导入成功', message: `已更新全局配置(未检测到页面)` }) |
There was a problem hiding this comment.
Derive success from settled results, not requests.length.
After Promise.allSettled(requests), hasOps only means work was attempted. A fully rejected overwrite/create batch still refreshes the tree, switches pages, and shows “导入成功”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/toolbars/upload/src/Main.vue` around lines 411 - 428, After awaiting
Promise.allSettled(requests) don't use requests.length to decide success;
instead capture the settled results (from Promise.allSettled) and compute
whether any operation fulfilled (e.g., results.some(r => r.status ===
'fulfilled')) and only when there is at least one fulfilled result call
pageSettingState.updateTreeData, select a page via switchToPageByName and call
useNotify with the "导入成功" message; if all settled results are rejected, avoid
refreshing/switching and call useNotify (or other error handling) to surface the
failure and optionally log the aggregate rejection reasons; update the block
that defines hasOps to use the settled results status and handle partial success
vs total failure accordingly, referencing requests, Promise.allSettled,
pageSettingState.updateTreeData, switchToPageByName, useNotify, and
state.pendingImportedPages.
| <span style="background-color: [object Event]; color: #8a8e99; font-size: 12px" | ||
| >温馨提示:页面左上角切换区域</span | ||
| > |
There was a problem hiding this comment.
Invalid CSS value: [object Event] is not a valid background-color.
This appears to be a JavaScript object accidentally serialized into the style attribute. This will cause a CSS parsing error and the background-color property will be ignored.
Proposed fix
- <span style="background-color: [object Event]; color: `#8a8e99`; font-size: 12px"
+ <span style="color: `#8a8e99`; font-size: 12px"Or specify the intended color value.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <span style="background-color: [object Event]; color: #8a8e99; font-size: 12px" | |
| >温馨提示:页面左上角切换区域</span | |
| > | |
| <span style="color: `#8a8e99`; font-size: 12px" | |
| >温馨提示:页面左上角切换区域</span | |
| > |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/vue-to-dsl/test/full/input/appdemo01/src/views/createVm.vue` around
lines 49 - 51, The inline style on the span rendering "温馨提示:页面左上角切换区域" contains
an invalid background-color value "[object Event]" (likely an accidentally
serialized event object); locate that span in createVm.vue and replace the
background-color token with a valid CSS color (e.g., a hex, rgb(), or named
color) or remove the inline style and move styling into a class/CSS rule (update
the className or stylesheet accordingly) so the background-color is a proper CSS
value.
| style="border-radius: 0px" | ||
| > | ||
| <tiny-form-item label="数据盘" style="border-radius: 0px"> | ||
| <div v-for="() in state.dataDisk" style="margin-top: 12px; display: flex"> |
There was a problem hiding this comment.
Invalid v-for syntax: empty loop variable.
v-for="() in state.dataDisk" is invalid Vue syntax. The directive requires at least one iteration variable.
Proposed fix
- <div v-for="() in state.dataDisk" style="margin-top: 12px; display: flex">
+ <div v-for="(_, index) in state.dataDisk" :key="index" style="margin-top: 12px; display: flex">Note: A :key binding should also be added for list rendering stability.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div v-for="() in state.dataDisk" style="margin-top: 12px; display: flex"> | |
| <div v-for="(_, index) in state.dataDisk" :key="index" style="margin-top: 12px; display: flex"> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/vue-to-dsl/test/full/input/appdemo01/src/views/createVm.vue` at line
292, The v-for on the div is using an empty loop variable ("v-for=\"() in
state.dataDisk\"") which is invalid; change it to provide iteration variables
(for example v-for="(disk, index) in state.dataDisk") and add a stable :key (for
example :key="disk.id || index"); also update any references inside the loop
that assumed a specific name to use the new variable (disk or index) where
needed.
| @@ -0,0 +1,24 @@ | |||
| import { defineConfig } from 'vite' | |||
| import path from 'path' | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check current vite config patterns in the repo for __dirname vs import.meta.url usage.
# Expected: confirm whether this file is an outlier and standardize accordingly.
fd '^vite\.config\.(js|ts|mjs|cjs)$' -x rg -n "__dirname|fileURLToPath|new URL\(" {}Repository: opentiny/tiny-engine
Length of output: 4711
🏁 Script executed:
cat -n packages/vue-to-dsl/test/full/input/appdemo01/vite.config.jsRepository: opentiny/tiny-engine
Length of output: 707
🏁 Script executed:
# Check if this is a CommonJS or ESM file, and verify directory structure
file packages/vue-to-dsl/test/full/input/appdemo01/vite.config.js
head -20 packages/vue-to-dsl/test/full/input/appdemo01/vite.config.jsRepository: opentiny/tiny-engine
Length of output: 554
🏁 Script executed:
# Check if there are similar test/demo vite.config files and how they handle this
fd 'vite\.config' packages/vue-to-dsl/test -x head -15 {}Repository: opentiny/tiny-engine
Length of output: 399
🏁 Script executed:
# Check the build configuration for this package to understand how it's compiled
cat -n packages/vue-to-dsl/package.json | head -50Repository: opentiny/tiny-engine
Length of output: 1821
🏁 Script executed:
# Search for references to this vite.config.js file
rg "appdemo01" packages/vue-to-dsl/test --type js --type ts -A 2 -B 2Repository: opentiny/tiny-engine
Length of output: 1566
🏁 Script executed:
# Check the test directory structure to understand what this fixture is used for
find packages/vue-to-dsl/test/full/input/appdemo01 -type f | head -20Repository: opentiny/tiny-engine
Length of output: 1373
🏁 Script executed:
# Check if there's a test that imports or uses this vite config
rg "vite\.config" packages/vue-to-dsl/test --type js --type ts -B 3 -A 3Repository: opentiny/tiny-engine
Length of output: 46
🏁 Script executed:
# Check if __dirname is declared anywhere in the file or imported
cat -n packages/vue-to-dsl/test/full/input/appdemo01/vite.config.jsRepository: opentiny/tiny-engine
Length of output: 707
🏁 Script executed:
# Verify the package.json type setting
grep -A 5 '"type"' packages/vue-to-dsl/package.jsonRepository: opentiny/tiny-engine
Length of output: 445
🏁 Script executed:
# Check if there are other test fixture vite configs and how they handle __dirname
find packages -path "*/test/*" -name "vite.config.*" -exec grep -l "__dirname\|fileURLToPath" {} \;Repository: opentiny/tiny-engine
Length of output: 296
🏁 Script executed:
# Check the other test fixture vite configs to see how they handle __dirname
for file in packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/vite.config.js packages/vue-generator/test/testcases/generator/expected/appdemo01/vite.config.js; do
echo "=== $file ==="
cat -n "$file"
echo ""
doneRepository: opentiny/tiny-engine
Length of output: 1541
🏁 Script executed:
# Check vue-generator package.json to see if it also has "type": "module"
grep -A 2 '"type"' packages/vue-generator/package.jsonRepository: opentiny/tiny-engine
Length of output: 286
Fix ESM-safe path resolution in Vite config.
In an ESM context (package.json declares "type": "module"), __dirname is not available and causes a ReferenceError at runtime. Use the ESM-safe pattern instead:
♻️ Proposed refactor
import { defineConfig } from 'vite'
import path from 'path'
+import { fileURLToPath } from 'url'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
+const __dirname = new URL('.', import.meta.url).pathname
+
export default defineConfig({
resolve: {
alias: {
- '@': path.resolve(__dirname, 'src')
+ '@': fileURLToPath(new URL('./src', import.meta.url))
}
},Also applies to: 9-9
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/vue-to-dsl/test/full/input/appdemo01/vite.config.js` at line 2,
Replace any use of the CommonJS __dirname in the Vite config with the ESM-safe
pattern: import { fileURLToPath } from 'url' and derive __dirname via const
__dirname = path.dirname(fileURLToPath(import.meta.url)); update any
path.resolve/joins that reference __dirname accordingly (look for occurrences of
__dirname in this file and lines around the import path statement) so the config
runs under "type":"module" without ReferenceError.
| define: { | ||
| 'process.env': { ...process.env } | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify whether this app actually depends on process.env in source files.
# Expected: no required `process.env` usages in client code, or usages can be migrated to import.meta.env.
rg -n "process\.env|import\.meta\.env" \
packages/vue-to-dsl/test/full/input/appdemo01/src \
-g "*.js" -g "*.ts" -g "*.vue"Repository: opentiny/tiny-engine
Length of output: 194
Remove the unnecessary and overly permissive process.env injection.
The define block exposes all build-time environment variables to the client bundle, creating a security risk. The codebase already follows Vite best practices by using import.meta.env.VITE_APP_MOCK (found in src/lowcodeConfig/dataSource.js), making this injection redundant. Delete the define block entirely.
🔧 Proposed fix
export default defineConfig({
@@
- define: {
- 'process.env': { ...process.env }
- },
build: {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| define: { | |
| 'process.env': { ...process.env } | |
| }, | |
| export default defineConfig({ | |
| build: { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/vue-to-dsl/test/full/input/appdemo01/vite.config.js` around lines 13
- 15, Remove the overly permissive define block that injects process.env into
the client bundle; specifically delete the define: { 'process.env': {
...process.env } } entry in vite.config.js (remove references to define and
process.env in that file) since the codebase uses import.meta.env.VITE_APP_MOCK
in src/lowcodeConfig/dataSource.js and this injection is redundant and a
security risk.
| export default defineConfig({ | ||
| build: { | ||
| lib: { | ||
| entry: path.resolve(__dirname, 'cli.ts'), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -C2 '\b__dirname\b' packages/vue-to-dsl/vite.config.cli.mjs
rg -n -C2 'fileURLToPath\s*\(\s*import\.meta\.url\s*\)' packages/vue-to-dsl/vite.config.cli.mjsRepository: opentiny/tiny-engine
Length of output: 203
🏁 Script executed:
#!/bin/bash
# Read the full vite.config.cli.mjs file to understand context
cat -n packages/vue-to-dsl/vite.config.cli.mjs
# Check for any build configuration that might compile this file
fd -t f 'vite.config' packages/vue-to-dsl/ 2>/dev/null | head -20
# Look for any config compilation or preprocessing
rg -n 'vite.config.cli' . --max-count=10Repository: opentiny/tiny-engine
Length of output: 1182
🏁 Script executed:
#!/bin/bash
# Check if there are any test files or build logs
fd -t f 'package.json' packages/vue-to-dsl/ -exec grep -l 'build:cli' {} \;
# Look for any CI/test configuration
fd -t f '.github' | head -5
# Search for any Node.js config that might inject globals
rg -n 'NODE_OPTIONS\|--require\|register' . --max-count=10
# Check if __dirname errors are mentioned in issues
rg -i '__dirname.*undefined\|ReferenceError.*__dirname' . 2>/dev/null | head -5Repository: opentiny/tiny-engine
Length of output: 267
🏁 Script executed:
#!/bin/bash
# Check package.json in vue-to-dsl
cat packages/vue-to-dsl/package.json
# Look for CI configuration
find .github -name '*.yml' -o -name '*.yaml' 2>/dev/null | head -5
# Check for any test runs or workflows
fd -t f '\.yml$|\.yaml$' .github 2>/dev/null | head -10Repository: opentiny/tiny-engine
Length of output: 2045
🏁 Script executed:
#!/bin/bash
# Check the release workflow to see if build:cli is executed
cat .github/workflows/Release.yml | head -100
# Also check if there are any other workflows
fd -t f '\.yml$' .github/workflows | xargs head -50Repository: opentiny/tiny-engine
Length of output: 8944
Use ESM-safe dirname resolution in Vite config.
__dirname is undefined in ESM modules. Line 8 will cause a ReferenceError when running vite build --config vite.config.cli.mjs.
Fix
import { defineConfig } from 'vite'
import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = path.dirname(__filename)
// 专用于打包 CLI 入口 cli.ts,输出为 dist/cli.js(CJS)
export default defineConfig({📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| entry: path.resolve(__dirname, 'cli.ts'), | |
| import { defineConfig } from 'vite' | |
| import path from 'node:path' | |
| import { fileURLToPath } from 'node:url' | |
| const __filename = fileURLToPath(import.meta.url) | |
| const __dirname = path.dirname(__filename) | |
| // 专用于打包 CLI 入口 cli.ts,输出为 dist/cli.js(CJS) | |
| export default defineConfig({ | |
| entry: path.resolve(__dirname, 'cli.ts'), | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/vue-to-dsl/vite.config.cli.mjs` at line 8, Replace the ESM-unsafe
use of __dirname in the Vite config entry (the line using
path.resolve(__dirname, 'cli.ts')) with an ESM-safe dirname computed from
import.meta.url; add an import reference to fileURLToPath from 'url' and compute
const __dirname = path.dirname(fileURLToPath(import.meta.url)) at the top of the
module, then use that __dirname in path.resolve for the entry so the config
works under ESM.
English | 简体中文
PR
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
Background and solution
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information
Summary by CodeRabbit