Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: materials new protocol #940

Merged
merged 27 commits into from
Jan 15, 2025

Conversation

yy-wow
Copy link
Collaborator

@yy-wow yy-wow commented Dec 17, 2024

English | 简体中文

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • 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

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • 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?

Issue Number: N/A

What is the new behavior?

物料资产包协议修改。涉及画布渲染、预览、出码、npm类型工具类的加载
协议内容如下:
json { "data": { "framework": "Vue", "materials": { "components": [ { "id": '', "component": 'TinyButton' "npm": { "pacakge": "@opentiny/vue", // "version": "3.11.0", // 移除 // "script" : "", // 移除 // "css": "", // 移除 "exportName": "Button" } } ], "blocks": [], "snippets": [], "packages": [ // 新增字段 { "name": "TinyVue组件库", // 名称 "package": "@opentiny/vue", // npm包名 "version": "3.11.0", // npm包版本 "script": "https://xxxxx", // esm格式的js文件cdn地址 "css": ["https://xxxxx"], // 样式表文件cdn地址 ] }, { "name": "xxx工具库", // 名称 "package": "lodash", // npm包名 "version": "3.11.0", // npm包版本 "script": "https://xxxxx", // esm格式的js文件cdn地址 "css": ["https://xxxxx"], // 样式表文件cdn地址 "needDeclare": true // 出码时是否需要声明到package.json的依赖,默认为true。当物料npm包中已声明的依赖且不需要在宿主工程声明时,可设置为false }, ] } } }

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced dependency management for component libraries.
    • Added support for dynamic component and utility loading.
    • Introduced new functions for managing utility and material dependencies.
    • Added new packages for TinyVue and element-plus libraries.
  • Improvements

    • Streamlined resource and material dependency processing.
    • Improved import map handling for canvas and materials.
    • Updated package management approach for third-party libraries.
    • Refined canvas initialization and dependency injection mechanisms.
    • Enhanced messaging and resource handling capabilities.
  • Bug Fixes

    • Corrected typos in function names.
    • Resolved issues with component and utility imports.
  • Chores

    • Refactored utility and resource management functions.
    • Updated import and export statements across multiple components.

Copy link
Contributor

coderabbitai bot commented Dec 17, 2024

Walkthrough

This pull request introduces a comprehensive restructuring of dependency management across multiple components and files. The changes primarily focus on how component libraries, scripts, and styles are imported, managed, and referenced. Key modifications include updating the bundle.json file to include new package entries, refactoring dependency loading mechanisms in various components, and introducing more flexible methods for handling material and utility dependencies. The changes aim to create a more centralized and dynamic approach to managing external resources and component libraries.

Changes

File Change Summary
designer-demo/public/mock/bundle.json Removed version, script, and CSS properties for components; added new package entries for TinyVue and element-plus
mockServer/src/mock/get/app-center/v1/apps/schema/918.json Updated axios CDN link, corrected dataHandler function name, expanded components tree and map
packages/canvas/DesignCanvas/src/DesignCanvas.vue Converted canvasSrcDoc to reactive reference, updated initialization logic with message subscription
packages/canvas/DesignCanvas/src/importMap.js Added canvasDeps parameter to getImportMapData function to support dynamic dependency inclusion
packages/canvas/common/src/utils.js Refactored dynamic import functionality, introduced new getComponents method
packages/canvas/container/src/CanvasContainer.vue Updated dependency management to focus on componentsDeps instead of thirdPartyDeps
packages/canvas/container/src/container.js Changed event dispatch from thirdPartyDeps to componentsDepsMap during canvas initialization
packages/common/js/preview.js Updated method for retrieving scripts and styles, introduced new utility dependencies handling
packages/design-core/src/preview/src/preview/Preview.vue Removed addUtilsImportMap function, simplified import map management
packages/plugins/materials/src/composable/useMaterial.js Restructured dependency management, added new functions for handling materials and utilities
packages/plugins/materials/src/composable/useResource.js Added materialsDeps property to state management, introduced getUtilsDeps function
packages/plugins/materials/src/meta/block/src/BlockGroupPanel.vue Removed useMaterial import and related dependency update logic
packages/plugins/bridge/src/js/resource.js Updated saveResource function to improve asynchronous handling
packages/canvas/render/src/runner.ts Removed dynamic import logic, simplified dependency handling
packages/canvas/render/src/application-function/utils.ts Updated setUtils function to handle asynchronous utility imports
packages/canvas/render/type.d.ts Added componentsDeps property to the Window interface for managing component dependencies

Possibly related PRs

  • fix: element-plus 物料适配问题修复与文档补充 #841: This PR updates the documentation for the Element Plus library, which is relevant to the changes in the main PR that involve the bundle.json file and the addition of new components and properties related to Element Plus.
  • fix: sync material to cli #921: This PR addresses configuration issues related to the bundle.json file, which aligns with the main PR's focus on updating the same file and modifying component configurations.
  • fix: fix exportName mismatch #929: This PR fixes an export name mismatch in the bundle.json, which is directly related to the changes made in the main PR that also involves updates to the same file.

Suggested reviewers

  • hexqi
  • chilingling

Poem

🐰 Hopping through code with glee,
Dependencies dance, now wild and free!
Scripts and styles, a magical brew,
Refactored magic, our canvas anew!
Tiny Vue leaps, with components so bright,
Coding rabbits dance in pure delight! 🎉


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 29fa9de and 0df006e.

📒 Files selected for processing (2)
  • packages/canvas/DesignCanvas/src/DesignCanvas.vue (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (7 hunks)
🧰 Additional context used
📓 Learnings (2)
packages/canvas/DesignCanvas/src/DesignCanvas.vue (1)
Learnt from: yy-wow
PR: opentiny/tiny-engine#940
File: packages/canvas/DesignCanvas/src/DesignCanvas.vue:0-0
Timestamp: 2025-01-15T02:19:06.755Z
Learning: In Vue components using message subscriptions from @opentiny/tiny-engine-meta-register, always clean up subscriptions in the onUnmounted hook using useMessage().unsubscribe() to prevent memory leaks.
packages/plugins/materials/src/composable/useMaterial.js (1)
Learnt from: yy-wow
PR: opentiny/tiny-engine#940
File: packages/plugins/materials/src/composable/useMaterial.js:0-0
Timestamp: 2025-01-13T07:49:12.136Z
Learning: In the materials system, componentsDepsMap.scripts in materialState is used as a data store for tracking component dependencies and doesn't require Vue reactivity as changes are handled explicitly through updateCanvasDeps().
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (11)
packages/canvas/DesignCanvas/src/DesignCanvas.vue (3)

68-68: LGTM! Good use of Vue's reactivity system.

Converting canvasSrcDoc to a reactive ref is the correct approach for managing reactive state in the Vue 3 Composition API.


243-247: LGTM! Proper cleanup of message subscription.

Good practice implementing the subscription cleanup in the onUnmounted hook to prevent memory leaks.


70-82: 🛠️ Refactor suggestion

Add validation and error handling to the subscription callback.

The subscription callback should be more robust to handle edge cases and prevent errors.

 useMessage().subscribe({
   topic: 'init_canvas_deps',
   subscriber: 'canvas_design_canvas',
   callback: (deps) => {
     if (canvasSrc) {
       return
     }
+    // Validate deps parameter
+    if (!deps || typeof deps !== 'object') {
+      console.error('Invalid dependencies received')
+      return
+    }
+    
+    // Prevent multiple initializations
+    if (canvasSrcDoc.value) {
+      return
+    }
+    
+    try {
       const { importMap, importStyles } = getImportMapData(getMergeMeta('engine.config')?.importMapVersion, deps)
       canvasSrcDoc.value = initCanvas(importMap, importStyles).html
+    } catch (error) {
+      console.error('Failed to initialize canvas:', error)
+      useNotify({
+        type: 'error',
+        title: 'Canvas initialization failed',
+        message: error.message
+      })
+    }
   }
 })

Likely invalid or redundant comment.

packages/plugins/materials/src/composable/useMaterial.js (8)

22-23: LGTM! Well-structured state management.

The new imports and state properties are well-organized. Using a Set for styles is an efficient choice for managing unique entries.

Also applies to: 42-43


435-436: LGTM! Good addition of dependency update.

The addition of updateCanvasDeps ensures that the canvas is notified of new dependencies after material fetching.


508-509: LGTM! Well-documented exports.

The new exports are clearly documented and maintain consistency with the existing interface.


293-298: 🛠️ Refactor suggestion

Add error handling for message publishing.

The function should handle potential publish failures and validate the dependency data.

 const updateCanvasDeps = () => {
+  try {
+    const deps = getCanvasDeps()
+    if (!deps.scripts.length && !deps.styles.length) {
+      console.warn('No dependencies to update')
+      return
+    }
     useMessage().publish({
       topic: 'init_canvas_deps',
-      data: getCanvasDeps()
+      data: deps
     })
+  } catch (error) {
+    console.error('Failed to update canvas dependencies:', error)
+  }
 }

Likely invalid or redundant comment.


449-452: 🛠️ Refactor suggestion

Add validation for npm object properties.

The function directly accesses npm properties without validation. Add proper checks to prevent potential runtime errors.

-      const { package: pkg, script, css, components } = npm
+      if (!npm?.package) {
+        console.warn('Invalid package structure:', npm)
+        return
+      }
+      const { package: pkg, script, css, components } = npm
       const npmInfo = materialState.componentsDepsMap.scripts.find((item) => item.package === pkg)

Likely invalid or redundant comment.


281-288: 🛠️ Refactor suggestion

Add null checks for appSchemaState access.

The function assumes appSchemaState.materialsDeps always exists. Add proper validation to handle edge cases.

 const getCanvasDeps = () => {
+  const materialsDeps = useResource().appSchemaState?.materialsDeps
+  if (!materialsDeps) {
+    return { scripts: [], styles: [] }
+  }
-  const { scripts, styles } = useResource().appSchemaState.materialsDeps
+  const { scripts = [], styles = [] } = materialsDeps

   return {
     scripts: [...scripts].filter((item) => item.script),
     styles: [...styles]
   }
 }

Likely invalid or redundant comment.


460-462: 🛠️ Refactor suggestion

Add array validation before iteration.

Verify that styles is an array before attempting to iterate over it.

-  if (Array.isArray(styles)) {
-    styles.forEach((item) => materialState.componentsDepsMap.styles.add(item))
-  }
+  if (Array.isArray(styles)) {
+    styles.filter(Boolean).forEach((item) => materialState.componentsDepsMap.styles.add(item))
+  }

Likely invalid or redundant comment.


301-347: 🛠️ Refactor suggestion

Add input validation and error handling in parseMaterialsDependencies.

The function needs better input validation and error handling for edge cases.

 const parseMaterialsDependencies = (materialBundle) => {
+  if (!materialBundle || typeof materialBundle !== 'object') {
+    console.warn('Invalid materialBundle')
+    return
+  }
   const { packages, components } = materialBundle
+  if (!Array.isArray(packages) && !Array.isArray(components)) {
+    console.warn('No valid dependencies found in materialBundle')
+    return
+  }

   const { scripts: scriptsDeps, styles: stylesDeps } = useResource().appSchemaState.materialsDeps
+  if (!scriptsDeps || !stylesDeps) {
+    console.error('Invalid dependency state structure')
+    return
+  }

   packages?.forEach((pkg) => {
+    try {
+      if (!pkg?.package) {
+        console.warn('Invalid package entry:', pkg)
+        return
+      }
       if (!pkg.script || scriptsDeps.find((item) => item.package === pkg.package)) {
         return
       }

       scriptsDeps.push(pkg)

       if (!pkg.css) {
         return
       }

       if (Array.isArray(pkg.css)) {
-        pkg.css.forEach((item) => stylesDeps.add(item))
+        pkg.css.filter(Boolean).forEach((item) => stylesDeps.add(item))
       } else {
         stylesDeps.add(pkg.css)
       }
+    } catch (error) {
+      console.error(`Error processing package ${pkg?.package}:`, error)
+    }
   })

   // Parse component npm field (compatible with old material protocol)
   const { scripts, styles } = generateThirdPartyDeps(components)
   scripts.forEach((item) => {
+    try {
       const dep = scriptsDeps.find((dep) => dep.package === item.package)
       if (dep) {
+        if (!item.components) {
+          console.warn(`No components found for package ${item.package}`)
+          return
+        }
         dep.components = { ...dep.components, ...item.components }
       } else {
         scriptsDeps.push(item)
       }
+    } catch (error) {
+      console.error(`Error merging dependencies for ${item?.package}:`, error)
+    }
   })

   if (!styles) {
     return
   }

   if (Array.isArray(styles)) {
-    styles.forEach((item) => stylesDeps.add(item))
+    styles.filter(Boolean).forEach((item) => stylesDeps.add(item))
   } else {
+    if (styles) {
       stylesDeps.add(styles)
+    }
   }
 }

Likely invalid or redundant comment.

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the refactor-main refactor/develop branch feature label Dec 17, 2024
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: 8

🔭 Outside diff range comments (3)
packages/canvas/common/src/utils.js (2)

Line range hint 107-116: Handle undefined modules when setting components

When dynamicImportComponents returns an empty object, modules[exportName] may be undefined. This could lead to issues when assigning components. Consider checking if modules[exportName] exists before assigning it.

Apply this diff to fix the issue:

export const setComponents = async ({ package: pkg, script, components }) => {
  if (!pkg) return

  const modules = await dynamicImportComponents(pkg, script)

  Object.entries(components).forEach(([componentId, exportName]) => {
    if (!window.TinyLowcodeComponent[componentId]) {
+     if (modules[exportName]) {
        window.TinyLowcodeComponent[componentId] = modules[exportName]
+     } else {
+       console.warn(`Component "${exportName}" not found in module "${pkg}".`)
+     }
    }
  })
}

Line range hint 125-131: Fix incorrect argument passing to dynamicImportComponents

In updateDependencies, the scripts.map(dynamicImportComponents) call passes the entire script object as the first argument, which doesn't match the expected parameters. Destructure the script objects to pass the correct arguments.

Apply this diff to fix the issue:

export const updateDependencies = ({ detail }) => {
  const { scripts = [], styles = [] } = detail || {}
  const { styles: canvasStyles } = window.componentsDepsMap
  const newStyles = [...styles].filter((item) => !canvasStyles.has(item))

  newStyles.forEach((item) => canvasStyles.add(item))

- const promises = [...newStyles].map((src) => addStyle(src)).concat(scripts.map(dynamicImportComponents))
+ const promises = [...newStyles]
+   .map((src) => addStyle(src))
+   .concat(scripts.map(({ package: pkg, script }) => dynamicImportComponents(pkg, script)))

  Promise.allSettled(promises)
}
packages/plugins/materials/src/composable/useMaterial.js (1)

Line range hint 417-437: Avoid mutating state while iterating in getBlockDeps

Modifying materialState.componentsDepsMap.scripts while iterating over scripts may lead to unexpected behavior. Collect new scripts separately before updating the state.

Apply this diff:

const getBlockDeps = (dependencies = {}) => {
  const { scripts = [], styles = [] } = dependencies
+ const newScripts = []

  scripts.length &&
    scripts.forEach((npm) => {
      const { package: pkg, script, css, components } = npm
      const npmInfo = materialState.componentsDepsMap.scripts.find((item) => item.package === pkg)

      if (!npmInfo || !npmInfo.script) {
-       materialState.componentsDepsMap.scripts.push({ package: pkg, script, css, components })
+       newScripts.push({ package: pkg, script, css, components })
      } else {
        const components = npmInfo.components || {}
        npmInfo.components = { ...components, ...npm.components }
      }
    })

+ materialState.componentsDepsMap.scripts.push(...newScripts)
  styles?.forEach((item) => materialState.componentsDepsMap.styles.add(item))
}
🧹 Nitpick comments (10)
packages/canvas/common/src/utils.js (1)

79-100: Improve error handling in dynamicImportComponents

If neither the import map includes the package nor a script URL is provided, the modules object remains empty, which may cause issues downstream. Consider adding a warning or error handling to notify about the missing module.

You can add a warning message when the module fails to load:

const dynamicImportComponents = async (pkg, script) => {
  if (window.TinyComponentLibs[pkg]) {
    return window.TinyComponentLibs[pkg]
  }

  let modules = {}

  try {
    // 优先从importmap导入,兼容npm.script字段定义的cdn地址
    if (getImportMapKeys().includes(pkg)) {
      modules = await import(/* @vite-ignore */ pkg)
    } else if (script) {
      modules = await import(/* @vite-ignore */ script)
+   } else {
+     console.warn(`Module "${pkg}" not found in import map and no script URL provided.`)
    }
  } catch (error) {
    modules = {}
  }

  window.TinyComponentLibs[pkg] = modules

  return modules
}
packages/plugins/materials/src/composable/useMaterial.js (1)

295-318: Consolidate dependency aggregation logic

The logic in setCanvasDeps for aggregating dependencies is similar to other parts of the code. Consider creating a utility function to handle dependency aggregation to avoid duplication.

packages/canvas/DesignCanvas/src/importMap.js (2)

Line range hint 3-46: Update function documentation to reflect new parameter

The function getImportMapData now accepts a second parameter canvasDeps. Update the JSDoc comment to include this parameter and its purpose.

Apply this diff:

/**
 * Generates import map data for the canvas.
+ * @param {Object} overrideVersions - Optional versions to override default import map versions.
+ * @param {Object} canvasDeps - Dependencies from the canvas, including scripts and styles.
 */
export function getImportMapData(overrideVersions = {}, canvasDeps = { scripts: [], styles: [] }) {

30-35: Initialize materialRequire imports properly

Ensure that materialRequire.imports is always an object to prevent potential undefined errors when spreading into importMap.imports.

Apply this diff:

const materialRequire = canvasDeps.scripts.reduce((imports, { package: pkg, script }) => {
  imports[pkg] = script
  return imports
}, {})

const importMap = {
  imports: {
    vue: `${VITE_CDN_DOMAIN}/vue@${importMapVersions.vue}/dist/vue.runtime.esm-browser.prod.js`,
    'vue-i18n': `${VITE_CDN_DOMAIN}/vue-i18n@${importMapVersions.vueI18n}/dist/vue-i18n.esm-browser.js`,
    ...blockRequire.imports,
    ...tinyVueRequire.imports,
+   ...materialRequire
  }
}
packages/vue-generator/src/plugins/genDependenciesPlugin.js (1)

48-54: Consider adding package validation

While the package processing logic is correct, it might benefit from additional validation to ensure package integrity.

Consider adding validation:

  packages.forEach((item) => {
    const { package: packageName, version } = item

+   if (!packageName) {
+     console.warn('Invalid package entry:', item)
+     return
+   }
+
+   if (version && !isValidSemver(version)) {
+     console.warn(`Invalid version '${version}' for package '${packageName}'`)
+     return
+   }

    if (packageName && !resDeps[packageName]) {
      resDeps[packageName] = version || 'latest'
    }
  })

You'll need to add the following import:

import { valid as isValidSemver } from 'semver'
packages/canvas/DesignCanvas/src/DesignCanvas.vue (3)

29-29: Consider grouping related imports

Consider grouping useMessage with other related hooks for better code organization.


61-74: Consider extracting initialization logic

The canvas initialization logic could be extracted into a separate function for better maintainability and reusability.

+const initCanvasDeps = (deps) => {
+  if (canvasSrc) {
+    return
+  }
+  const { importMap, importStyles } = getImportMapData(
+    getMergeMeta('engine.config')?.importMapVersion,
+    deps
+  )
+  canvasSrcDoc.value = initCanvas(importMap, importStyles).html
+}
+
 useMessage().subscribe({
   topic: 'init_canvas_deps',
-  callback: (deps) => {
-    if (canvasSrc) {
-      return
-    }
-    const { importMap, importStyles } = getImportMapData(
-      getMergeMeta('engine.config')?.importMapVersion,
-      deps
-    )
-    canvasSrcDoc.value = initCanvas(importMap, importStyles).html
-  }
+  callback: initCanvasDeps
 })

Line range hint 82-147: Consider extracting complex conditions

The watch logic contains complex nested conditions. Consider extracting these into named functions or constants for better readability.

Example approach:

const shouldShowConfirm = (isSaved, pageSchema, oldPageSchema) => 
  (!isSaved || pageSchema !== oldPageSchema) && !showModal

const isValidPageStatus = (pageStatus, pageSchema) =>
  ![PAGE_STATUS.Guest, PAGE_STATUS.Occupy].includes(pageStatus.state) &&
  pageSchema?.componentName
designer-demo/public/mock/bundle.json (2)

43-43: Consider using object notation for shortcuts and contextMenu actions.

The current array syntax for properties and actions could be converted to object notation for better maintainability and type safety.

- "properties": ["type", "size"]
+ "properties": {
+   "type": true,
+   "size": true
+ }

- "actions": ["copy", "remove", "insert", "updateAttr", "bindEevent", "createBlock"]
+ "actions": {
+   "copy": true,
+   "remove": true,
+   "insert": true,
+   "updateAttr": true,
+   "bindEevent": true,
+   "createBlock": true
+ }

Also applies to: 46-46, 49-49


Line range hint 13258-13271: Consider adding value validation for checkbox group.

The default values and options should be validated to ensure they match.

 "modelValue": ["name1", "name2"],
+"validator": {
+  "type": "array",
+  "items": {
+    "enum": ["name1", "name2", "name3"]
+  }
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 230b334 and 7b910c5.

📒 Files selected for processing (15)
  • designer-demo/public/mock/bundle.json (51 hunks)
  • mockServer/src/mock/get/app-center/v1/apps/schema/918.json (1 hunks)
  • packages/canvas/DesignCanvas/src/DesignCanvas.vue (2 hunks)
  • packages/canvas/DesignCanvas/src/importMap.js (2 hunks)
  • packages/canvas/common/src/utils.js (2 hunks)
  • packages/canvas/container/src/CanvasContainer.vue (1 hunks)
  • packages/canvas/container/src/container.js (1 hunks)
  • packages/canvas/render/src/RenderMain.js (1 hunks)
  • packages/canvas/render/src/runner.js (2 hunks)
  • packages/common/js/preview.js (1 hunks)
  • packages/design-core/src/preview/src/preview/Preview.vue (1 hunks)
  • packages/plugins/bridge/src/js/resource.js (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (9 hunks)
  • packages/toolbars/generate-code/src/Main.vue (1 hunks)
  • packages/vue-generator/src/plugins/genDependenciesPlugin.js (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/canvas/container/src/container.js
🔇 Additional comments (13)
packages/plugins/materials/src/composable/useMaterial.js (1)

403-413: ⚠️ Potential issue

Initialize materialState.dependencies to ensure it is an array

In fetchMaterial, materialState.dependencies may not be properly initialized if packages is undefined. Ensure it is always an array to prevent potential errors.

Apply this diff:

const packages = []

materials.forEach((response) => {
  if (response.status === 'fulfilled' && response.value.materials) {
    addMaterials(response.value.materials)
    packages.push(...(response.value.materials.packages || []))
  }
})

- materialState.dependencies = packages
+ materialState.dependencies = packages || []
setCanvasDeps()

Likely invalid or redundant comment.

packages/common/js/preview.js (1)

Line range hint 29-36: LGTM! Verify dependency mapping consistency

The renaming from thirdPartyDeps to componentsDepsMap is consistent with the PR's objectives. The logic for processing scripts and styles is maintained correctly.

Let's verify the consistency of this change across the codebase:

✅ Verification successful

Dependency mapping change is consistently implemented

The renaming from thirdPartyDeps to componentsDepsMap has been thoroughly verified. The new componentsDepsMap is properly integrated across the codebase:

  • Used in material state management (useMaterial.js)
  • Correctly propagated to canvas components
  • Properly handled in preview functionality
  • No lingering references to the old thirdPartyDeps name
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining references to thirdPartyDeps
rg "thirdPartyDeps" --type js --type vue

# Verify the new componentsDepsMap usage
rg "componentsDepsMap" --type js --type vue

Length of output: 140


Script:

#!/bin/bash
# Check for any remaining references to thirdPartyDeps, searching all text files
rg "thirdPartyDeps"

# Verify the new componentsDepsMap usage, searching all text files
rg "componentsDepsMap"

Length of output: 1749

packages/canvas/render/src/runner.js (1)

14-14: LGTM! Import statement updated correctly

The import statement has been updated to include setComponents, maintaining consistency with the new dependency management approach.

packages/vue-generator/src/plugins/genDependenciesPlugin.js (2)

25-25: LGTM! Schema destructuring updated correctly

The addition of the packages array to the schema destructuring aligns with the new material asset package protocol.


48-54: Verify package resolution strategy

The current implementation might need to consider package resolution conflicts between different sources (utils, componentsMap, and packages).

Let's analyze the package resolution strategy:

✅ Verification successful

Let me analyze the current implementation and verification results:

  1. The code in question handles package dependencies by storing them in resDeps object
  2. From the search results, I can see:
    • The package resolution happens in multiple places (utils, componentsMap, packages)
    • There are multiple components using the same package with different versions (e.g. @opentiny/vue has versions 0.1.16, 0.1.17, 3.2.0, 3.4.1)

Let me check the full implementation of the dependency resolution:


After analyzing the full implementation, I can now provide a final response:

Package resolution strategy is correctly implemented

The current implementation has a well-defined precedence order that prevents conflicts:

  1. Utils dependencies are processed first, with duplicate prevention via resDeps[packageName] check
  2. Component dependencies are added only if:
    • The package isn't already in resDeps
    • The component is actually used (verified via componentsSet)
  3. Additional packages are added last, also with duplicate prevention
  4. Special handling for @opentiny/vue-icon to match @opentiny/vue version

This strategy ensures that:

  • No duplicate packages are added
  • Earlier declarations take precedence
  • Only dependencies for used components are included
  • Version consistency is maintained for related packages
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential package conflicts in the codebase
rg -A 5 "package: .*," --type js --type json

# Look for existing version resolution logic
rg "version.*latest" --type js

Length of output: 85133


Script:

#!/bin/bash
# Check the full implementation of genDependenciesPlugin
cat packages/vue-generator/src/plugins/genDependenciesPlugin.js

Length of output: 2744

packages/plugins/bridge/src/js/resource.js (3)

14-21: LGTM! Well-organized imports

The addition of useMaterial import aligns with the PR's objective of modifying the material asset package protocol.


188-221: Well-structured async/await implementation with proper error handling

The conversion to async/await and addition of try/catch improves error handling. The conditional handling of npm type resources with setCanvasDeps() aligns with the new material package protocol.


224-227: LGTM! Clear user feedback implementation

The success notification implementation provides clear feedback with dynamic messages based on the action type.

packages/design-core/src/preview/src/preview/Preview.vue (1)

83-83: LGTM! Simplified import map handling

The direct usage of store.setImportMap simplifies the code by removing unnecessary intermediate steps, making it more maintainable.

packages/toolbars/generate-code/src/Main.vue (1)

148-149: LGTM: Addition of packages array to appSchema

The addition of the packages array aligns with the PR objectives to support the new material asset package protocol.

Let's verify the packages array structure in other files:

✅ Verification successful

LGTM: Package array structure is consistent across the codebase

Based on the search results, the packages array structure is consistently defined and used across the codebase:

  • Mock data in designer-demo/public/mock/bundle.json shows the expected structure with name, package, version, and script properties
  • The structure is properly consumed in vue-generator for dependency generation
  • The implementation in useMaterial.js correctly handles and propagates the packages array
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the structure of packages array across the codebase
# Expected: Consistent structure with name, npm package name, version, and CDN links

# Search for packages array definitions
rg -A 5 "packages.*\["

Length of output: 3097

mockServer/src/mock/get/app-center/v1/apps/schema/918.json (1)

2095-2096: Verify the axios version for security concerns

The addition of CDN link for axios is good, but we should verify if version 1.7.9 is the latest stable version without known vulnerabilities.

Let's check the version:

✅ Verification successful

Axios version 1.7.9 is secure and up-to-date

The version check reveals that:

  • 1.7.9 is the current latest stable version from npm
  • The most recent high severity vulnerability affected versions <= 1.7.3 (patched in 1.7.4)
  • The specified version 1.7.9 is above the vulnerable range and includes all security patches
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify axios version security
# Expected: No known vulnerabilities for the specified version

# Check for security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "axios", severities: [CRITICAL, HIGH]) {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

# Get latest version from npm
curl -s https://registry.npmjs.org/axios/latest | jq '.version'

Length of output: 1066

designer-demo/public/mock/bundle.json (2)

14081-14088: Verify package dependency configuration.

The new packages array introduces element-plus dependency configuration. While the structure looks good, we should verify the version consistency across components using this package.

✅ Verification successful

Package version consistency verified across the codebase

The version "2.4.2" of element-plus is consistently used across all instances in the codebase, including:

  • designer-demo/public/mock/bundle.json
  • mockServer/src/assets/json/appinfo.json
  • packages/engine-cli/template/designer/public/mock/bundle.json
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if all element-plus component definitions use consistent version
rg -A 1 '"package": "element-plus"' | grep -A 1 'version'

Length of output: 1561


635-635: Validate form item nesting rules.

The nesting rules for ElForm should be verified to ensure proper form structure.

Also applies to: 644-644, 647-647, 650-650

✅ Verification successful

Form nesting rules are properly defined in the configuration

The configuration correctly specifies that ElFormItem components are allowed as children within ElForm components through the childWhitelist property. This is consistently defined across multiple configuration files in the codebase, indicating proper form structure validation rules are in place.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if ElFormItem is properly nested under ElForm
ast-grep --pattern 'componentName: "ElForm" {
  $$$
  componentName: "ElFormItem"
  $$$
}'

Length of output: 92


Script:

#!/bin/bash
# Search for form nesting rules in bundle.json
rg -A 5 '"childWhitelist".*ElFormItem' public/mock/bundle.json

Length of output: 128


Script:

#!/bin/bash
# Search for component definitions related to ElForm and ElFormItem
rg -A 10 '"componentName":\s*"ElForm"' public/mock/bundle.json

Length of output: 128


Script:

#!/bin/bash
# Look for any validation or nesting rules
rg -i "form.*item.*nest|nest.*form.*item" public/mock/bundle.json

Length of output: 131


Script:

#!/bin/bash
# Find the correct path to bundle.json
fd bundle.json

Length of output: 216


Script:

#!/bin/bash
# Search for ElForm in all json files
rg -A 5 '"componentName":\s*"ElForm"' --type json

Length of output: 574


Script:

#!/bin/bash
# Search for childWhitelist in all json files
rg -A 5 '"childWhitelist".*ElFormItem' --type json

Length of output: 1522

packages/canvas/common/src/utils.js Outdated Show resolved Hide resolved
packages/canvas/render/src/RenderMain.js Outdated Show resolved Hide resolved
packages/canvas/render/src/RenderMain.js Outdated Show resolved Hide resolved
packages/plugins/materials/src/composable/useMaterial.js Outdated Show resolved Hide resolved
packages/plugins/materials/src/composable/useMaterial.js Outdated Show resolved Hide resolved
packages/canvas/render/src/runner.js Outdated Show resolved Hide resolved
packages/plugins/bridge/src/js/resource.js Outdated Show resolved Hide resolved
packages/canvas/container/src/CanvasContainer.vue Outdated Show resolved Hide resolved
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: 4

🔭 Outside diff range comments (1)
packages/canvas/common/src/utils.js (1)

Line range hint 110-117: Add error handling for undefined components and module exports to prevent runtime exceptions

In the setComponents function, there's a possibility that components or modules[exportName] may be undefined. Attempting to access or assign these values without validation could lead to runtime errors. Please add checks to ensure that these variables are defined before proceeding.

Apply this diff to add necessary checks:

 export const setComponents = async ({ package: pkg, script, components }) => {
   if (!pkg) return

   const modules = await dynamicImportComponents(pkg, script)

+  if (!components || typeof components !== 'object') {
+    console.error('Components object is undefined or invalid')
+    return
+  }

   Object.entries(components).forEach(([componentId, exportName]) => {
+    if (!modules || !modules[exportName]) {
+      console.error(`Module '${exportName}' not found in package '${pkg}'`)
+      return
+    }
     if (!window.TinyLowcodeComponent[componentId]) {
       window.TinyLowcodeComponent[componentId] = modules[exportName]
     }
   })
 }
🧹 Nitpick comments (4)
packages/canvas/common/src/utils.js (1)

63-71: Add a null check for document.querySelector to prevent potential errors when the import map is missing

In the getImportMapKeys function, if the <script type="importmap"> element does not exist in the document, document.querySelector will return null, leading to a TypeError when attempting to access textContent. While the try...catch block handles the error by returning an empty array, it's better to explicitly check for the element's existence to avoid unnecessary exceptions.

Apply this diff to add a null check:

 const getImportMapKeys = () => {
   try {
-    const importMaps = document.querySelector('script[type="importmap"]').textContent
+    const importMapScript = document.querySelector('script[type="importmap"]')
+    if (!importMapScript) {
+      return []
+    }
+    const importMaps = importMapScript.textContent
     const importMapObject = JSON.parse(importMaps)
 
     return Object.keys(importMapObject.import)
   } catch (error) {
     return []
   }
 }
packages/plugins/materials/src/composable/useMaterial.js (2)

Line range hint 212-255: Rename setThirdPartyDeps to setComponentsDeps for consistency

The function generateThirdPartyDeps has been renamed to generateComponentsDeps, but setThirdPartyDeps still retains the old naming convention. For consistency and clarity, consider renaming setThirdPartyDeps to setComponentsDeps.

Apply this diff to rename the function:

-const setThirdPartyDeps = (components) => {
+const setComponentsDeps = (components) => {
   const { scripts = [], styles = [] } = generateComponentsDeps(components)
-  materialState.componentsDepsMap.scripts.push(...scripts)
+  materialState.componentsDepsMap.scripts.push(...scripts)
   styles.forEach((item) => materialState.componentsDepsMap.styles.add(item))
 }

Don't forget to update all references to setThirdPartyDeps in the codebase:

#!/bin/bash
# Description: Update references from `setThirdPartyDeps` to `setComponentsDeps`

# Replace all instances in the codebase
rg 'setThirdPartyDeps' --files-with-matches | xargs sed -i 's/setThirdPartyDeps/setComponentsDeps/g'

295-319: Handle duplicate packages when setting canvas dependencies

In the setCanvasDeps function, when reducing allPackages, duplicate packages are filtered out based on the package name. However, if different versions or scripts are provided for the same package, this could lead to inconsistent behavior. Consider handling version conflicts or consolidating scripts for the same package.

Apply this diff to improve duplicate package handling:

-      if (scripts.find((item) => item.package === pkg)) {
+      const existingPackage = scripts.find((item) => item.package === pkg)
+      if (existingPackage) {
+        // Merge components and other properties if necessary
+        existingPackage.components = { ...existingPackage.components, ...npm.components }
+        return { scripts, styles }
+      }
designer-demo/public/mock/bundle.json (1)

Line range hint 13258-13271: Consider adding validation for checkbox group values.

The default modelValue array contains strings but the schema doesn't enforce value types. Consider adding type validation.

 "modelValue": ["name1", "name2"],
+"type": {
+  "type": "array",
+  "items": {
+    "type": "string"
+  }
+},
 "type": "checkbox",
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 230b334 and 7b910c5.

📒 Files selected for processing (15)
  • designer-demo/public/mock/bundle.json (51 hunks)
  • mockServer/src/mock/get/app-center/v1/apps/schema/918.json (1 hunks)
  • packages/canvas/DesignCanvas/src/DesignCanvas.vue (2 hunks)
  • packages/canvas/DesignCanvas/src/importMap.js (2 hunks)
  • packages/canvas/common/src/utils.js (2 hunks)
  • packages/canvas/container/src/CanvasContainer.vue (1 hunks)
  • packages/canvas/container/src/container.js (1 hunks)
  • packages/canvas/render/src/RenderMain.js (1 hunks)
  • packages/canvas/render/src/runner.js (2 hunks)
  • packages/common/js/preview.js (1 hunks)
  • packages/design-core/src/preview/src/preview/Preview.vue (1 hunks)
  • packages/plugins/bridge/src/js/resource.js (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (9 hunks)
  • packages/toolbars/generate-code/src/Main.vue (1 hunks)
  • packages/vue-generator/src/plugins/genDependenciesPlugin.js (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/canvas/container/src/container.js
🔇 Additional comments (15)
packages/canvas/common/src/utils.js (2)

79-101: Verify that removing the export statement from dynamicImportComponents does not affect external modules

The dynamicImportComponents function was previously exported but is now declared without the export keyword. This change could break functionality in other modules that import this function. Please verify that no external modules rely on this export, or consider re-exporting the function if necessary.

Run the following script to check for external imports of dynamicImportComponents:


125-127: Ensure window.componentsDepsMap is properly initialized before accessing its properties

In the updateDependencies function, window.componentsDepsMap is used to access styles. Please verify that window.componentsDepsMap is initialized and contains the expected structure to prevent potential undefined errors.

Run the following script to check the initialization of window.componentsDepsMap:

packages/canvas/DesignCanvas/src/importMap.js (1)

Line range hint 3-47: LGTM!

The getImportMapData function has been correctly updated to accept canvasDeps, allowing dynamic inclusion of external dependencies in the import map and styles. The default parameters and object destructuring are used appropriately.

packages/common/js/preview.js (1)

Line range hint 29-36: Verify handling of empty or malformed dependency maps

The code assumes componentsDepsMap will always have valid scripts and styles arrays. Consider adding defensive checks.

-  const { scripts, styles } = useMaterial().materialState.componentsDepsMap
+  const { scripts = [], styles = [] } = useMaterial().materialState.componentsDepsMap || {}
   params.scripts = {}
   scripts
     .filter((item) => item.script)
     .forEach((item) => {
+      if (!item.package) return
       params.scripts[item.package] = item.script
     })
   params.styles = [...styles]

Let's verify the structure of componentsDepsMap across the codebase:

packages/vue-generator/src/plugins/genDependenciesPlugin.js (1)

48-54: Consider version conflict resolution strategy

The current implementation might lead to version conflicts when the same package is defined in multiple places (utils, componentsMap, packages).

Let's verify potential version conflicts in existing material definitions:

Consider implementing a version resolution strategy:

  1. Define priority order (e.g., packages > componentsMap > utils)
  2. Add version compatibility checks
  3. Log warnings for potential conflicts
packages/plugins/bridge/src/js/resource.js (3)

14-21: LGTM! Clean import organization

The addition of useMaterial aligns with the new material asset package protocol implementation.


189-203: ⚠️ Potential issue

Fix variable shadowing in try block

The result variable is declared both in the outer scope and inside the try block, which could lead to unexpected behavior.

Apply this diff to fix the variable shadowing:

-      const result = await requestUpdateReSource(data)
+      result = await requestUpdateReSource(data)

-      const result = await requestAddReSource(data)
+      result = await requestAddReSource(data)

Likely invalid or redundant comment.


213-221: Verify canvas dependency updates

The conditional handling of npm resources with setCanvasDeps() looks good, but let's verify that all canvas instances are properly updated.

✅ Verification successful

Let me gather more information about the canvas dependency update mechanisms to ensure completeness.


Based on the analysis of the shell script results, I can now provide the final response:

Canvas dependency updates are properly handled

The verification shows that the canvas dependency update mechanism is correctly implemented:

  • setCanvasDeps() in useMaterial() handles npm dependencies by updating both material dependencies and utility dependencies
  • The else branch with updateUtils([result]) correctly handles non-npm resources by updating the canvas utility functions
  • Both paths trigger appropriate canvas refreshes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of setCanvasDeps across the codebase
# Expected: setCanvasDeps should be called consistently after npm resource changes

# Check for setCanvasDeps usage
rg -A 5 "setCanvasDeps" 

# Check for other npm resource handling patterns
ast-grep --pattern 'if ($expr.type === "npm") { $$$ }'

Length of output: 1945


Script:

#!/bin/bash
# Check for updateUtils usage and related canvas update patterns
rg -A 5 "updateUtils" 

# Check for canvas refresh/update related functions
rg -A 5 "canvasApi" 

# Look for npm type handling in material state
ast-grep --pattern 'type === "npm"'

Length of output: 46980

packages/design-core/src/preview/src/preview/Preview.vue (1)

83-83: Verify cleanup of old import map utilities

The simplified import map management looks good, but we should verify that all references to the old utility functions have been removed.

✅ Verification successful

Based on the search results from our verification attempts, we can make a conclusion:

Import map utilities have been properly cleaned up

The searches show that:

  • Only the new setImportMap method is being used in Preview.vue
  • No instances of the old addUtilsImportMap were found anywhere in the codebase
  • The import map handling is now centralized through the store's setImportMap method
  • All other import map related code appears to be using the new simplified approach
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to old import map utilities
# Expected: No references to addUtilsImportMap should remain

# Search for any remaining references to the old function
rg "addUtilsImportMap"

# Check for any similar utility functions that might need updating
ast-grep --pattern 'function $name($_, $_) { 
  $$$
  importMap
  $$$
}'

Length of output: 97


Script:

#!/bin/bash
# Let's try a broader search for import map related functions and their usage
# Search for files containing 'importMap' keyword
rg -l "importMap"

# Then let's look for specific patterns related to import map manipulation
rg "\.importMap" -A 2 -B 2

# Search for store operations related to import maps
rg "setImportMap|addImportMap|updateImportMap" -A 2 -B 2

Length of output: 2076


Script:

#!/bin/bash
# Let's check the history of the importMap.js files to understand the changes
rg -A 10 -B 10 "addUtilsImportMap|setImportMap" packages/design-core/src/preview/src/preview/importMap.js packages/canvas/DesignCanvas/src/importMap.js

# Also check the store implementation to see how import maps are handled
rg -A 5 -B 5 "class.*Store" packages/design-core/src/preview/src/preview/

# Look for any utility functions related to import maps
ast-grep --pattern 'function $name($_) {
  $$$
  importMap
  $$$
}'

Length of output: 299

packages/canvas/DesignCanvas/src/DesignCanvas.vue (1)

61-61: LGTM! Good use of Vue's reactivity system

Converting canvasSrcDoc to a ref ensures proper reactivity for canvas updates.

mockServer/src/mock/get/app-center/v1/apps/schema/918.json (1)

2095-2096: Verify axios CDN link compatibility.

The addition of the CDN link for axios should be verified for:

  1. Version compatibility with the application
  2. CORS policy compliance
  3. Availability and reliability of the CDN
designer-demo/public/mock/bundle.json (2)

14081-14088: Verify the package configuration for element-plus.

The package configuration looks good but let's verify the unpkg URLs are accessible and the version is stable.

✅ Verification successful

Element-plus package configuration is secure and accessible

The verification shows:

  • Both CDN URLs (CSS and MJS) are accessible with 200 status codes
  • Version 2.4.2 is well above the patched version (2.0.6) for the only known vulnerability
  • The vulnerability was from April 2022 and has been addressed
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify element-plus package URLs and version
curl -I "https://unpkg.com/[email protected]/dist/index.full.mjs"
curl -I "https://unpkg.com/[email protected]/dist/index.css"

# Check for any security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "element-plus") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 2676


635-638: Verify nesting rule configuration for form components.

The nesting rules for ElForm and ElFormItem look correct, but let's verify there are no other form components that should be included in the whitelist.

Also applies to: 768-771

✅ Verification successful

Nesting rules for form components are correctly configured

The verification shows that only ElForm and ElFormItem are the form-related components in the bundle, and the current nesting rule configuration correctly allows ElFormItem as a child of ElForm. No additional form components need to be included in the whitelist.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other form-related components that may need to be included in nesting rules
ast-grep --pattern 'component: "El$_Form$_"'

Length of output: 46


Script:

#!/bin/bash
# Search for form-related components in the bundle.json file
rg -A 5 '"component":\s*"El.*Form' public/mock/bundle.json

# Also search for any form-related nesting rules
rg -A 5 '"childWhitelist".*Form' public/mock/bundle.json

Length of output: 247


Script:

#!/bin/bash
# Search for form-related components in the bundle.json file
rg -A 5 '"component":\s*"El.*Form' designer-demo/public/mock/bundle.json

# Also search for any form-related nesting rules
rg -A 5 '"childWhitelist".*Form' designer-demo/public/mock/bundle.json

Length of output: 804

packages/toolbars/generate-code/src/Main.vue (1)

148-149: Verify handling of new packages property in code generation

The addition of packages property to appSchema aligns with the new material asset protocol. However, we should ensure proper handling in the code generation process.

Let's verify the code generation handling:

Consider adding JSDoc documentation for the appSchema structure to help maintainers understand the new protocol format.

✅ Verification successful

The packages property is properly handled in code generation

The code review reveals that the packages property is safely integrated into the code generation flow:

  1. It's part of the appSchema object which is passed to generateAppCode
  2. The spread operator with fallback ([...(appData.packages || [])]) ensures safe handling of undefined values
  3. The property is used alongside other essential schema properties like componentsMap, pageSchema, and blockSchema
  4. The generated code is properly processed through generateAppCode and transformed into file content for local saving
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the code generation process handles the new packages property

# Search for packages property usage in code generation
rg -A 5 "packages.*=.*appSchema\.packages" 

# Search for potential validation or processing of packages
ast-grep --pattern 'function $_(appSchema) {
  $$$
  packages
  $$$
}'

Length of output: 117


Script:

#!/bin/bash
# Let's broaden our search to understand the usage of packages in code generation

# Search for any references to packages in the codebase
rg -A 5 "packages.*=" packages/toolbars/generate-code/

# Look for appSchema usage patterns
rg -A 5 "appSchema\." packages/toolbars/generate-code/

# Search for code generation related functions
ast-grep --pattern 'function generate$_($$$) {
  $$$
}'

# Check for any schema or type definitions
fd "\.d\.ts$|types?\.ts$" packages/toolbars/generate-code/ --exec cat {}

Length of output: 248


Script:

#!/bin/bash
# Let's check the broader context of the file and its usage

# Get the content of Main.vue to understand the context
cat packages/toolbars/generate-code/src/Main.vue

# Look for files that might consume the generated output
fd . packages/toolbars/generate-code/ -e .vue -e .ts -e .js --exclude "*.test.*" --exclude "*.spec.*" --exec rg -l "getPreGenerateInfo|appSchema"

# Search for any package-related types or interfaces
rg -g "*.ts" -g "*.d.ts" -g "*.vue" "interface.*App.*Schema|type.*App.*Schema" packages/toolbars/generate-code/

Length of output: 7776

packages/canvas/container/src/CanvasContainer.vue (1)

116-116: ⚠️ Potential issue

Breaking Change: Verify migration from thirdPartyDeps to componentsDepsMap

The change from thirdPartyDeps to componentsDepsMap is a breaking change that requires careful verification of all consumers.

Let's verify the migration completeness:

Please ensure all consumers of this window property are updated. Consider:

  1. Adding a migration guide for downstream dependencies
  2. Documenting the new componentsDepsMap structure
  3. Adding a deprecation warning for any transitional period

packages/canvas/render/src/RenderMain.js Outdated Show resolved Hide resolved
packages/canvas/render/src/runner.js Outdated Show resolved Hide resolved
@yy-wow yy-wow added the ready_for_review This PR requires more reviews label Dec 18, 2024
@yy-wow yy-wow changed the title Feat/materials new protocol Feat: materials new protocol Dec 18, 2024
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

🧹 Nitpick comments (5)
packages/canvas/render/src/RenderMain.js (3)

70-82: Consider enhancing error handling for function utilities.

The default function () => {} might silently hide errors when the function generation fails. Consider logging these failures or notifying users about malformed function definitions.

-      const defaultFn = () => {}
-      utilsCollection[item.name] = generateFunction(item.content.value, context) || defaultFn
+      try {
+        const fn = generateFunction(item.content.value, context)
+        if (!fn) {
+          globalNotify({
+            type: 'warning',
+            message: `Function utility "${item.name}" could not be generated`
+          })
+        }
+        utilsCollection[item.name] = fn || (() => {})
+      } catch (error) {
+        globalNotify({
+          type: 'error',
+          message: `Failed to generate function utility "${item.name}": ${error.message}`
+        })
+        utilsCollection[item.name] = () => {}
+      }

84-102: Consider internationalizing error messages and adding retry mechanism.

The implementation handles npm imports well but has a few areas for improvement:

  1. Error message is hardcoded in Chinese
  2. No retry mechanism for failed imports
-      globalNotify({
-        type: 'error',
-        message: `工具类 ${npmUtils[index].name} 加载失败,请检查CDN地址是否配置正确`
-      })
+      const message = t('error.npm_utility_load_failed', {
+        name: npmUtils[index].name
+      })
+      globalNotify({
+        type: 'error',
+        message,
+        action: {
+          text: t('action.retry'),
+          onClick: () => retryImport(npmUtils[index])
+        }
+      })

Line range hint 441-486: Document breaking changes in API signatures.

The transition of setUtils and related functions from synchronous to asynchronous is a breaking change that affects API consumers. This needs to be clearly documented.

Consider:

  1. Adding JSDoc comments to document the async nature of these functions
  2. Updating the API documentation
  3. Adding migration guides for existing consumers

Example JSDoc:

+/**
+ * Sets utility functions and npm modules in the canvas context.
+ * @async
+ * @param {Array} data - Array of utility configurations
+ * @param {boolean} clear - Whether to clear existing utilities
+ * @param {boolean} isForceRefresh - Whether to force a canvas refresh
+ * @returns {Promise<void>}
+ * @throws {Error} When npm module import fails
+ */
 const setUtils = async (data, clear, isForceRefresh) => {
packages/canvas/common/src/utils.js (2)

59-61: Enhance JSDoc documentation.

The current documentation could be more descriptive. Consider adding @returns {string[]} and more detailed description.

/**
- * 从页面importmap中获取模块的名称
- * @returns importmap中模块的名称集合
+ * Retrieves module names from the page's import map configuration
+ * @returns {string[]} Array of module names defined in the import map, or empty array if not found
+ */

Line range hint 113-124: Add error handling for missing exports and consider cache invalidation.

The function should verify that the requested component exports exist and consider cache invalidation strategy.

Consider these improvements:

 export const getComponents = async ({ package: pkg, script, components }) => {
   if (!pkg) return
 
   const modules = await dynamicImportComponentLib({ pkg, script })
 
   Object.entries(components).forEach(([componentId, exportName]) => {
+    if (!(exportName in modules)) {
+      console.warn(`Component "${exportName}" not found in package "${pkg}"`)
+      return
+    }
     if (!window.TinyLowcodeComponent[componentId]) {
       window.TinyLowcodeComponent[componentId] = modules[exportName]
     }
   })
 }

Also, consider adding a cache invalidation mechanism to handle component updates.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b910c5 and 6c49614.

📒 Files selected for processing (2)
  • packages/canvas/common/src/utils.js (2 hunks)
  • packages/canvas/render/src/RenderMain.js (1 hunks)
🔇 Additional comments (2)
packages/canvas/render/src/RenderMain.js (1)

Line range hint 109-120: Add missing await keywords to async function calls.

The updateUtils and deleteUtils functions call the async setUtils without awaiting its completion. This could lead to race conditions.

Apply this diff to fix the async calls:

-const updateUtils = (data) => {
+const updateUtils = async (data) => {
-  setUtils(data, false, true)
+  await setUtils(data, false, true)
 }

-const deleteUtils = (data) => {
+const deleteUtils = async (data) => {
   data?.forEach((item) => {
     if (utils[item.name]) {
       delete utils[item.name]
     }
   })
-  setUtils([], false, true)
+  await setUtils([], false, true)
 }
packages/canvas/common/src/utils.js (1)

73-73: Verify the correct property name in importMapObject.

The code accesses importMapObject.imports but comments mention "import". Verify that "imports" is the correct property name according to the Import Maps specification.

✅ Verification successful

Let me gather more information about import maps usage and specification in the codebase.


Let me check the content of the import map JSON file to verify the property name.


Property name "imports" is correct according to Import Maps specification

The code correctly uses importMapObject.imports as the property name. This is verified by:

  1. The import map JSON file which uses "imports" as the root property
  2. Multiple consistent usages across the codebase in files like externalDeps.js, copyImportMap.js, and others
  3. This follows the standard Import Maps specification where "imports" is the correct property name for specifying module specifier remapping
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other import map usages to verify property name
rg -g '*.{js,ts,vue}' -A 2 -B 2 'importmap.*imports'

Length of output: 408


Script:

#!/bin/bash
# Search for importmap related files and configurations
rg -g '*.{js,ts,vue,json}' -i 'importmap'

# Look for any import map JSON files
fd -e json 'importmap'

# Search for import map related documentation or comments
rg -g '*.{js,ts,vue,md}' -i 'import map' -C 3

Length of output: 11260


Script:

#!/bin/bash
# Check the content of the import map JSON file
cat packages/design-core/src/preview/src/preview/importMap.json

# Look for any Import Maps specification references in documentation
rg -g '*.md' -i 'import maps? specification'

Length of output: 1893

packages/canvas/common/src/utils.js Outdated Show resolved Hide resolved
packages/canvas/common/src/utils.js Outdated Show resolved Hide resolved
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

🔭 Outside diff range comments (2)
packages/plugins/materials/src/meta/block/src/BlockGroupPanel.vue (1)

Line range hint 135-143: Consider improving promise chain handling

The current promise chain could be optimized for better error handling and race condition prevention:

-          }).then(() => {
+          }).then(async () => {
             isRefresh.value = true
             state.searchValue = ''
             selectedBlockArray.value.length = 0
-            useResource().fetchResource({ isInit: false }) // 添加区块分组,不需要重新init页面或者区块。
+            await useResource().fetchResource({ isInit: false }) // 添加区块分组,不需要重新init页面或者区块。
             useNotify({
               message: '添加区块成功',
               type: 'success'
             })

This change ensures that:

  1. The resource fetch completes before showing the success notification
  2. Any errors in resource fetching are properly caught by the catch block
packages/plugins/materials/src/composable/useMaterial.js (1)

Line range hint 451-471: Improve type safety in getBlockDeps

The function should validate input types and handle potential undefined values.

Apply this diff:

 const getBlockDeps = (dependencies = {}) => {
   const { scripts = [], styles = [] } = dependencies
 
+  if (!Array.isArray(scripts) || !Array.isArray(styles)) {
+    return
+  }
 
   scripts.length &&
     scripts.forEach((npm) => {
+      if (!npm?.package) {
+        return
+      }
       const { package: pkg, script, css, components } = npm
       const npmInfo = materialState.componentsDepsMap.scripts.find((item) => item.package === pkg)
 
       if (!npmInfo || !npmInfo.script) {
         materialState.componentsDepsMap.scripts.push({ package: pkg, script, css, components })
       } else {
         const components = npmInfo.components || {}
         npmInfo.components = { ...components, ...npm.components }
       }
     })
 
   styles?.forEach((item) => materialState.componentsDepsMap.styles.add(item))
 }
🧹 Nitpick comments (4)
packages/plugins/materials/src/meta/block/src/BlockGroupPanel.vue (2)

Line range hint 28-39: Consider extracting block map logic to a composable

The block map initialization logic could be moved to a separate composable for better reusability and maintainability.

Consider creating a new composable useBlockMap.js:

// useBlockMap.js
import { ref } from 'vue'

export function useBlockMap() {
  const blockMap = ref(new Map())

  const initGroupBlockMap = (groups = []) => {
    blockMap.value.clear()
    for (let group of groups) {
      const groupBlock = group?.blocks || []
      for (let block of groupBlock) {
        blockMap.value.set(block.id, block)
      }
    }
  }

  const includesBlockInGroups = (blockId) => blockMap.value.has(blockId)

  return {
    initGroupBlockMap,
    includesBlockInGroups
  }
}

This would:

  1. Improve code organization
  2. Make the functionality more reusable
  3. Make testing easier

Line range hint 213-234: Consider optimizing API calls and data processing

The current implementation makes multiple separate API calls and processes results individually. This could be optimized for better performance.

-      Promise.allSettled([fetchTenants(), fetchUsers(), fetchTags()]).then((results) => {
+      const staticTenants = [
+        { name: '对所有组织开放', id: '1' },
+        { name: '对当前组织开放', id: '2' }
+      ]
+
+      Promise.all([fetchUsers(), fetchTags()])
+        .then(([users, tags]) => {
           state.filters[0].children = [
-          {
-            name: '对所有组织开放',
-            id: '1'
-          },
-          {
-            name: '对当前组织开放',
-            id: '2'
-          }
+          ...staticTenants
         ]
-        state.filters[1].children =
-          results[1].status === 'fulfilled'
-            ? results[1].value.map((item) => ({
-                name: item?.username,
-                id: item?.id
-              }))
-            : []
-        state.filters[2].children =
-          results[2].status === 'fulfilled' ? results[2].value.map((item) => ({ name: item })) : []
-        blockUsers.value = state.filters[1].children
+        state.filters[1].children = users.map(item => ({
+          name: item?.username,
+          id: item?.id
+        }))
+        state.filters[2].children = tags.map(item => ({ name: item }))
+        blockUsers.value = state.filters[1].children
+      })
+      .catch(error => {
+        message({
+          message: `Failed to fetch filters: ${error.message || error}`,
+          status: 'error'
+        })
       })

This change:

  1. Removes unnecessary fetchTenants call since the data is static
  2. Uses Promise.all instead of Promise.allSettled for simpler error handling
  3. Adds proper error handling
  4. Simplifies the data processing logic
packages/plugins/materials/src/composable/useMaterial.js (2)

446-447: Consider error handling in fetchMaterial

The function should handle potential errors when calling setCanvasDeps.

Apply this diff:

-  setCanvasDeps()
+  try {
+    setCanvasDeps()
+  } catch (error) {
+    console.error('Failed to set canvas dependencies:', error)
+  }

485-485: Ensure safe access to canvas API

The optional chaining on canvasApi.value is good, but consider adding a null check for detail.

Apply this diff:

-  useCanvas().canvasApi.value?.canvasDispatch('updateDependencies', { detail: materialState.componentsDepsMap })
+  const deps = materialState.componentsDepsMap
+  if (deps?.scripts?.length || deps?.styles?.size) {
+    useCanvas().canvasApi.value?.canvasDispatch('updateDependencies', { detail: deps })
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c971b2b and b277596.

📒 Files selected for processing (8)
  • packages/canvas/container/src/CanvasContainer.vue (2 hunks)
  • packages/canvas/container/src/container.js (0 hunks)
  • packages/canvas/render/src/runner.js (2 hunks)
  • packages/common/js/preview.js (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (8 hunks)
  • packages/plugins/materials/src/composable/useResource.js (1 hunks)
  • packages/plugins/materials/src/meta/block/src/BlockGroupPanel.vue (1 hunks)
  • packages/vue-generator/src/plugins/genDependenciesPlugin.js (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/canvas/container/src/container.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/canvas/render/src/runner.js
  • packages/common/js/preview.js
  • packages/vue-generator/src/plugins/genDependenciesPlugin.js
  • packages/canvas/container/src/CanvasContainer.vue
🔇 Additional comments (4)
packages/plugins/materials/src/composable/useResource.js (1)

36-37: LGTM: New state property for canvas dependencies

The addition of canvasDeps with separate collections for scripts and styles is a good approach. Using Set for styles ensures uniqueness and prevents duplicates.

packages/plugins/materials/src/composable/useMaterial.js (3)

41-42: Update state management for dependencies

Good refactoring to replace thirdPartyDeps with componentsDepsMap. The new structure better represents the relationship between components and their dependencies.


274-288: ⚠️ Potential issue

Enhance error handling in getUtilsDependencies

The function should handle potential undefined properties in the utility content.

Apply this diff:

 return utils
-  .filter((item) => item.type === 'npm' && item.content.cdnLink)
+  .filter((item) => item.type === 'npm' && item.content?.cdnLink)
   .map((item) => {
-    const { package: pkg, version, cdnLink } = item.content
+    const { package: pkg = '', version = '', cdnLink = '' } = item.content || {}
     return {
       package: pkg,
       version,
       script: cdnLink
     }
   })

Likely invalid or redundant comment.


293-309: Verify dependency updates in setCanvasDeps

The function needs to ensure that duplicate scripts are not added and that the message payload is properly structured.

✅ Verification successful

Duplicate prevention and message structure are properly implemented

The code implementation in setCanvasDeps is correct and safe:

  • It properly checks for duplicates using scripts.find() before adding new utilities
  • The message payload structure is consistent with its consumer in DesignCanvas.vue
  • The scripts array is properly initialized and managed throughout the file
  • The spread operator [...styles] creates a new array, preventing unintended mutations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential duplicate script entries
ast-grep --pattern 'useMessage().publish({ topic: "init_canvas_deps", data: $_ })'

Length of output: 84


Script:

#!/bin/bash
# Let's try a different pattern to match the message publishing
ast-grep --pattern 'useMessage().publish({
  topic: "init_canvas_deps",
  data: $$$
})'

# Also search for other usages of init_canvas_deps
rg "init_canvas_deps" -A 3

# Check the structure of getUtilsDependencies
ast-grep --pattern 'getUtilsDependencies = () => $$$'

Length of output: 796


Script:

#!/bin/bash
# Let's check how scripts array is populated and used
rg "scripts\." -A 3 packages/plugins/materials/src/composable/useMaterial.js

# Check the structure of useResource and resState
ast-grep --pattern 'const { scripts, styles } = useResource().resState.canvasDeps'

# Look for the definition of scripts array
rg "scripts =" packages/plugins/materials/src/composable/useMaterial.js

Length of output: 1489

packages/plugins/materials/src/composable/useMaterial.js Outdated Show resolved Hide resolved
packages/canvas/common/src/utils.js Outdated Show resolved Hide resolved
packages/canvas/container/src/CanvasContainer.vue Outdated Show resolved Hide resolved
packages/canvas/render/src/RenderMain.js Outdated Show resolved Hide resolved
packages/plugins/materials/src/composable/useMaterial.js Outdated Show resolved Hide resolved
packages/plugins/materials/src/composable/useMaterial.js Outdated Show resolved Hide resolved
@hexqi hexqi changed the title Feat: materials new protocol feat: materials new protocol Jan 9, 2025
@github-actions github-actions bot added the enhancement New feature or request label Jan 9, 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: 0

🧹 Nitpick comments (9)
packages/canvas/render/src/RenderMain.js (4)

99-103: Internationalize error messages

The error message is hardcoded in Chinese. Consider using i18n for better internationalization support.

-      message: `工具类 ${npmUtils[index].name} 加载失败,请检查CDN地址是否配置正确`
+      message: t('utils.loadError', { name: npmUtils[index].name })

97-105: Enhance error handling and logging

Consider adding detailed error logging and structured error handling.

 results.forEach((res, index) => {
   if (res.status !== 'fulfilled') {
+    console.error(`Failed to load utility ${npmUtils[index].name}:`, res.reason)
     globalNotify({
       type: 'error',
-      message: `工具类 ${npmUtils[index].name} 加载失败,请检查CDN地址是否配置正确`
+      message: t('utils.loadError', { name: npmUtils[index].name }),
+      details: res.reason?.message
     })
     return
   }

87-92: Validate function-type utilities

Consider adding validation for function-type utilities to ensure they meet expected interfaces.

 data
   .filter((item) => item.type === 'function')
   .forEach((item) => {
     const defaultFn = () => {}
+    const fn = generateFunction(item.content.value, context)
+    if (fn && typeof fn !== 'function') {
+      console.warn(`Utility ${item.name} is not a function`)
+      return
+    }
-    utilsCollection[item.name] = generateFunction(item.content.value, context) || defaultFn
+    utilsCollection[item.name] = fn || defaultFn
   })

106-111: Validate npm module exports

Consider adding validation for npm module exports to ensure they meet expected interfaces.

 const module = res.value
 const { name, content } = npmUtils[index]
 const { exportName, destructuring } = content

+const exportedValue = destructuring ? module[exportName] : module.default
+if (exportedValue === undefined) {
+  console.warn(`Export ${exportName} not found in module ${name}`)
+  return
+}
-utilsCollection[name] = destructuring ? module[exportName] : module.default
+utilsCollection[name] = exportedValue
packages/plugins/materials/src/composable/useMaterial.js (2)

304-320: Add debouncing for canvas dependency updates.

The setCanvasDeps function might be called frequently during initialization. Consider debouncing the message publishing.

Apply this diff to add debouncing:

+const debouncedPublish = utils.debounce((data) => {
+  useMessage().publish({
+    topic: 'init_canvas_deps',
+    data
+  })
+}, 100)

 const setCanvasDeps = () => {
   const { scripts, styles } = useResource().resState.canvasDeps

   getUtilsDependencies().forEach((util) => {
     if (scripts.find((item) => util.package === item.package)) {
       return
     }

     scripts.push(util)
   })

-  useMessage().publish({
-    topic: 'init_canvas_deps',
-    data: { scripts: scripts, styles: [...styles] }
-  })
+  debouncedPublish({ scripts: scripts, styles: [...styles] })
 }

468-479: Ensure atomic updates for dependency maps.

The block dependency handling should ensure atomic updates to prevent race conditions.

Apply this diff to ensure atomic updates:

-      materialState.componentsDepsMap.scripts.push({ package: pkg, script, css, components })
+      const newDeps = [...materialState.componentsDepsMap.scripts]
+      newDeps.push({ package: pkg, script, css, components })
+      materialState.componentsDepsMap.scripts = newDeps
designer-demo/public/mock/bundle.json (3)

14041-14047: Consider adding integrity hashes for CDN resources

For the TinyVue package CDN resources, consider adding integrity hashes to prevent tampering:

 {
   "name": "TinyVue组件库",
   "package": "@opentiny/vue",
   "version": "3.14.0",
   "destructuring": true,
   "script": "https://unpkg.com/@opentiny/vue@~3.14/runtime/tiny-vue.mjs",
+  "scriptIntegrity": "<sha384-hash>",
   "css": "https://unpkg.com/@opentiny/vue-theme@~3.14/index.css",
+  "cssIntegrity": "<sha384-hash>"
 }

14048-14054: Consider pinning exact versions for CDN URLs

The Element Plus package uses tilde ranges in CDN URLs which could potentially pull in breaking changes. Consider pinning to exact versions:

 {
   "name": "element-plus组件库",
   "package": "element-plus", 
   "version": "2.4.2",
-  "script": "https://unpkg.com/[email protected]/dist/index.full.mjs",
+  "script": "https://unpkg.com/[email protected]/dist/index.full.mjs",
   "css": "https://unpkg.com/[email protected]/dist/index.css"
 }

14039-14054: Consider adding package validation metadata

The packages section could benefit from additional metadata to help validate package integrity and compatibility:

 "packages": [
   {
     "name": "TinyVue组件库",
     "package": "@opentiny/vue",
     "version": "3.14.0",
+    "engines": {
+      "node": ">=14.0.0",
+      "npm": ">=6.0.0"
+    },
+    "peerDependencies": {
+      "vue": "^3.0.0"
+    }
   }
   // ... other packages
 ]
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b277596 and 27cb75e.

📒 Files selected for processing (13)
  • designer-demo/public/mock/bundle.json (33 hunks)
  • packages/canvas/DesignCanvas/src/DesignCanvas.vue (1 hunks)
  • packages/canvas/DesignCanvas/src/importMap.js (2 hunks)
  • packages/canvas/container/src/CanvasContainer.vue (2 hunks)
  • packages/canvas/container/src/container.js (0 hunks)
  • packages/canvas/render/src/RenderMain.js (2 hunks)
  • packages/canvas/render/src/runner.js (2 hunks)
  • packages/common/js/preview.js (2 hunks)
  • packages/design-core/src/preview/src/preview/Preview.vue (1 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (8 hunks)
  • packages/plugins/materials/src/composable/useResource.js (1 hunks)
  • packages/plugins/materials/src/meta/block/src/BlockGroupPanel.vue (1 hunks)
  • packages/toolbars/generate-code/src/Main.vue (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/canvas/container/src/container.js
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/toolbars/generate-code/src/Main.vue
  • packages/canvas/DesignCanvas/src/importMap.js
  • packages/plugins/materials/src/meta/block/src/BlockGroupPanel.vue
  • packages/canvas/container/src/CanvasContainer.vue
  • packages/common/js/preview.js
  • packages/plugins/materials/src/composable/useResource.js
🔇 Additional comments (10)
packages/canvas/render/src/RenderMain.js (1)

Line range hint 71-114: Consider handling importMap existence scenarios

Based on the previous review comments, the code should handle two scenarios:

  1. When importMap exists
  2. When importMap doesn't exist (should use cdnLink)

Let's verify the importMap handling:

Consider refactoring the npm utils import logic to handle both scenarios:

 const npmUtils = data.filter((item) => item.type === 'npm' && item.content.cdnLink)
-const results = await Promise.allSettled(npmUtils.map((item) => import(/* @vite-ignore */ item.content.package)))
+const results = await Promise.allSettled(npmUtils.map(async (item) => {
+  const { content } = item
+  const importPath = window.host?.importMap?.[content.package] || content.cdnLink
+  return import(/* @vite-ignore */ importPath)
+}))
packages/canvas/render/src/runner.js (2)

14-14: LGTM! Import statement updated to support new dependency management.

The import statement has been updated to include new utilities that support the revised dependency management approach.


84-85: 🛠️ Refactor suggestion

Verify error handling for dependency loading.

While the dependency loading logic is sound, consider adding error handling to gracefully handle component loading failures.

Apply this diff to improve error handling:

   Promise.all([
-    ...window.componentsDeps.map(getComponents),
-    ...scripts.map((src) => addScript(src)).concat(styles.map((src) => addStyle(src)))
+    ...window.componentsDeps.map(dep => 
+      getComponents(dep).catch(err => {
+        console.error(`Failed to load component:`, err);
+        return null;
+      })
+    ),
+    ...scripts.map(src => 
+      addScript(src).catch(err => {
+        console.error(`Failed to load script: ${src}`, err);
+        return null;
+      })
+    ),
+    ...styles.map(src => 
+      addStyle(src).catch(err => {
+        console.error(`Failed to load style: ${src}`, err);
+        return null;
+      })
+    )
   ]).finally(() => create(config))
packages/design-core/src/preview/src/preview/Preview.vue (1)

83-83: LGTM! Simplified import map handling.

The direct usage of store.setImportMap(importMapData) improves code clarity by removing unnecessary complexity.

packages/canvas/DesignCanvas/src/DesignCanvas.vue (2)

62-62: LGTM! Enhanced reactivity with ref().

Converting canvasSrcDoc to use ref() ensures proper reactivity in Vue's composition API.


64-75: 🛠️ Refactor suggestion

Add error handling for dependency initialization.

The message subscription implementation should handle potential errors during import map generation.

Apply this diff to improve error handling:

 useMessage().subscribe({
   topic: 'init_canvas_deps',
   callback: (deps) => {
     if (canvasSrc) {
       return
     }
+    try {
       const { importMap, importStyles } = getImportMapData(getMergeMeta('engine.config')?.importMapVersion, deps)
       canvasSrcDoc.value = initCanvas(importMap, importStyles).html
+    } catch (error) {
+      console.error('Failed to initialize canvas dependencies:', error)
+      // Consider emitting an error event to notify the parent
+      useMessage().publish({
+        topic: 'canvas_init_error',
+        data: { error }
+      })
+    }
   }
 })

Likely invalid or redundant comment.

packages/plugins/materials/src/composable/useMaterial.js (3)

42-43: LGTM! Improved state management structure.

The new componentsDepsMap structure provides better organization for scripts and styles dependencies.


285-299: 🛠️ Refactor suggestion

Add validation for utility dependencies.

The getUtilsDependencies function should validate the utility structure before accessing properties.

Apply this diff to add validation:

 const getUtilsDependencies = () => {
   const { utils } = useResource().resState

   return utils
-    .filter((item) => item.type === 'npm' && item.content.cdnLink)
+    .filter((item) => {
+      return item?.type === 'npm' && 
+             item?.content?.cdnLink && 
+             item?.content?.package
+    })
     .map((item) => {
       const { package: pkg, version, cdnLink } = item.content

       return {
         package: pkg,
         version,
         script: cdnLink
       }
     })
 }

Likely invalid or redundant comment.


323-366: 🛠️ Refactor suggestion

Add input validation for material dependencies.

The parseMaterialsDependencies function should validate the input structure.

Apply this diff to add validation:

 const parseMaterialsDependencies = (materialBundle) => {
+  if (!materialBundle || typeof materialBundle !== 'object') {
+    console.warn('Invalid material bundle provided')
+    return
+  }
   const { packages = [], components = [] } = materialBundle
+  if (!Array.isArray(packages) || !Array.isArray(components)) {
+    console.warn('Invalid packages or components structure')
+    return
+  }

   const { scripts: scriptsDeps, styles: stylesDeps } = useResource().resState.canvasDeps

   packages?.forEach((pkg) => {
+    if (!pkg?.package) {
+      console.warn('Invalid package structure:', pkg)
+      return
+    }
     if (scriptsDeps.find((item) => item.package === pkg.package)) {
       return
     }

     scriptsDeps.push(pkg)

     if (!pkg.css) {
       return
     }

     if (Array.isArray(pkg.css)) {
       pkg.css.map(stylesDeps.add)
     } else {
       stylesDeps.add(pkg.css)
     }
   })

Likely invalid or redundant comment.

designer-demo/public/mock/bundle.json (1)

14040-14054: Verify package versions and security advisories

The package configurations reference specific versions that should be verified for security and compatibility.

✅ Verification successful

Consider updating package versions for improved features and security

Both packages have newer versions available that include bug fixes and improvements:

  • @opentiny/vue: 3.14.0 → 3.21.0
  • element-plus: 2.4.2 → 2.9.2

No critical security vulnerabilities found in current versions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for security advisories and latest versions of the specified packages

# Check latest versions
echo "Checking @opentiny/vue latest version..."
curl -s https://registry.npmjs.org/@opentiny/vue/latest | jq '.version'

echo "Checking element-plus latest version..."
curl -s https://registry.npmjs.org/element-plus/latest | jq '.version'

# Check for security advisories
echo "Checking security advisories..."
gh api graphql -f query='
{
  securityVulnerabilities(first: 10, ecosystem: NPM, package: "element-plus") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 998

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: 4

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27cb75e and 9ec89fb.

📒 Files selected for processing (1)
  • packages/plugins/bridge/src/js/resource.js (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (3)
packages/plugins/bridge/src/js/resource.js (3)

14-14: LGTM! Well-structured imports

The import statement is properly organized and includes all necessary utilities from the meta-register package.


178-179: LGTM! Improved code readability

Good use of async/await and the isEdit variable makes the code more maintainable.

Also applies to: 181-182


215-221: 🛠️ Refactor suggestion

Move success notification after all operations complete

The success notification should only be shown after confirming all operations (including canvas updates) have completed successfully.

Consider moving the success notification and state updates into a finally block:

-  useNotify({
-    type: 'success',
-    message: `${isEdit ? '修改' : '创建'}成功`
-  })
-  emit('refresh', state.type)
-  state.refresh = true
-  callback()
+  try {
+    // ... existing try blocks ...
+  } catch (error) {
+    // ... error handling ...
+  } finally {
+    if (!error) {
+      useNotify({
+        type: 'success',
+        message: `${isEdit ? '修改' : '创建'}成功`
+      })
+      emit('refresh', state.type)
+      state.refresh = true
+      callback()
+    }
+  }

Likely invalid or redundant comment.

packages/plugins/bridge/src/js/resource.js Outdated Show resolved Hide resolved
packages/plugins/bridge/src/js/resource.js Outdated Show resolved Hide resolved
packages/plugins/bridge/src/js/resource.js Outdated Show resolved Hide resolved
packages/plugins/bridge/src/js/resource.js Outdated Show resolved Hide resolved
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 (3)
packages/plugins/materials/src/composable/useMaterial.js (3)

42-43: Consider using TypeScript interfaces for state objects.

The componentsDepsMap and packages properties would benefit from TypeScript interfaces to ensure type safety and provide better documentation of the expected structure.

interface ComponentsDepsMap {
  scripts: Array<{
    package: string;
    script: string;
    components?: Record<string, string>;
  }>;
  styles: Set<string>;
}

interface MaterialState {
  components: any[];
  blocks: any[];
  componentsDepsMap: ComponentsDepsMap;
  packages: Array<{
    package: string;
    version: string;
    script?: string;
    css?: string | string[];
  }>;
}

304-320: Consider adding error handling for message publishing.

The function should handle potential errors during message publishing and provide feedback.

 const setCanvasDeps = () => {
   const { scripts, styles } = useResource().appSchemaState.canvasDeps
 
   getUtilsDependencies().forEach((util) => {
     if (scripts.find((item) => util.package === item.package)) {
       return
     }
 
     scripts.push(util)
   })
 
+  try {
     useMessage().publish({
       topic: 'init_canvas_deps',
       data: { scripts: scripts, styles: [...styles] }
     })
+  } catch (error) {
+    console.error('Failed to publish canvas dependencies:', error)
+  }
 }

467-471: Consider memory management for Set operations.

The Set of styles could grow indefinitely. Consider implementing a cleanup mechanism.

 const getBlockDeps = (dependencies = {}) => {
   const { scripts = [], styles = [] } = dependencies
+  const MAX_STYLES = 1000 // Adjust based on your needs
 
   scripts.length &&
     scripts.forEach((npm) => {
       const { package: pkg, script, css, components } = npm
       const npmInfo = materialState.componentsDepsMap.scripts.find((item) => item.package === pkg)
 
       if (!npmInfo || !npmInfo.script) {
         materialState.componentsDepsMap.scripts.push({ package: pkg, script, css, components })
       } else {
         const components = npmInfo.components || {}
         npmInfo.components = { ...components, ...npm.components }
       }
     })
 
-  styles?.forEach((item) => materialState.componentsDepsMap.styles.add(item))
+  styles?.forEach((item) => {
+    materialState.componentsDepsMap.styles.add(item)
+    if (materialState.componentsDepsMap.styles.size > MAX_STYLES) {
+      const iterator = materialState.componentsDepsMap.styles.values()
+      materialState.componentsDepsMap.styles.delete(iterator.next().value)
+    }
+  })
 }

Also applies to: 479-479

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec89fb and 50ef894.

📒 Files selected for processing (3)
  • packages/canvas/container/src/CanvasContainer.vue (2 hunks)
  • packages/common/js/preview.js (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/common/js/preview.js
  • packages/canvas/container/src/CanvasContainer.vue
🔇 Additional comments (2)
packages/plugins/materials/src/composable/useMaterial.js (2)

285-299: ⚠️ Potential issue

Add null checks for nested properties.

The function assumes item.content always exists and has a cdnLink property. This could lead to runtime errors.

Apply this diff to add proper null checks:

 return utils
-    .filter((item) => item.type === 'npm' && item.content.cdnLink)
+    .filter((item) => item.type === 'npm' && item.content?.cdnLink)
     .map((item) => {
-      const { package: pkg, version, cdnLink } = item.content
+      const { package: pkg, version = '', cdnLink = '' } = item.content || {}
 
       return {
         package: pkg,
         version,
         script: cdnLink
       }
     })

Likely invalid or redundant comment.


323-366: ⚠️ Potential issue

Add input validation and error handling.

The function should validate the input structure and handle potential errors.

Apply this diff to add proper validation:

 const parseMaterialsDependencies = (materialBundle) => {
+  if (!materialBundle || typeof materialBundle !== 'object') {
+    console.warn('Invalid material bundle provided')
+    return
+  }
   const { packages = [], components = [] } = materialBundle
 
   const { scripts: scriptsDeps, styles: stylesDeps } = useResource().appSchemaState.canvasDeps
 
   packages?.forEach((pkg) => {
+    if (!pkg?.package) {
+      console.warn('Invalid package configuration:', pkg)
+      return
+    }
     if (scriptsDeps.find((item) => item.package === pkg.package)) {
       return
     }

Likely invalid or redundant comment.

packages/plugins/materials/src/composable/useMaterial.js Outdated Show resolved Hide resolved
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

♻️ Duplicate comments (1)
packages/plugins/materials/src/composable/useMaterial.js (1)

493-493: ⚠️ Potential issue

Add error handling for canvas dispatch.

The canvas dispatch operation should handle potential errors when the canvas API is not available.

🧹 Nitpick comments (2)
packages/plugins/materials/src/composable/useMaterial.js (2)

42-43: Consider using TypeScript interfaces for state objects.

The reactive state objects would benefit from TypeScript interfaces to clearly define the shape of componentsDepsMap and packages. This would improve maintainability and provide better IDE support.

interface ComponentsDepsMap {
  scripts: Array<{
    package: string;
    script: string;
    components?: Record<string, string>;
  }>;
  styles: Set<string>;
}

interface MaterialState {
  components: any[];
  blocks: any[];
  componentsDepsMap: ComponentsDepsMap;
  packages: any[]; // Define specific package structure
}

540-540: Document the new exported functions.

The newly exported setCanvasDeps function lacks JSDoc documentation. Add documentation to maintain consistency with other exported functions.

+  /**
+   * Sets canvas dependencies including material and utility dependencies.
+   * Notifies the canvas to refresh the importmap.
+   */
   setCanvasDeps,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50ef894 and 775f6c9.

📒 Files selected for processing (1)
  • packages/plugins/materials/src/composable/useMaterial.js (8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (3)
packages/plugins/materials/src/composable/useMaterial.js (3)

285-299: ⚠️ Potential issue

Add error handling for missing or malformed utils.

The getUtilsDependencies function should handle potential errors when accessing item.content properties.

 const getUtilsDependencies = () => {
   const { utils } = useResource().appSchemaState
 
   return utils
-    .filter((item) => item.type === 'npm' && item.content.cdnLink)
+    .filter((item) => item.type === 'npm' && item.content?.cdnLink)
     .map((item) => {
+      if (!item.content?.package) {
+        console.warn(`Invalid util item: missing package name`, item)
+        return null
+      }
       const { package: pkg, version, cdnLink } = item.content
 
       return {
         package: pkg,
         version,
         script: cdnLink
       }
-    })
+    })
+    .filter(Boolean)

Likely invalid or redundant comment.


468-479: ⚠️ Potential issue

Ensure atomic updates for reactive state.

Direct mutations to materialState.componentsDepsMap may not trigger reactivity properly. Consider using atomic updates.

-materialState.componentsDepsMap.scripts.push({ package: pkg, script, css, components })
+const updatedScripts = [...materialState.componentsDepsMap.scripts, { package: pkg, script, css, components }]
+materialState.componentsDepsMap.scripts = updatedScripts

-styles?.forEach((item) => materialState.componentsDepsMap.styles.add(item))
+const updatedStyles = new Set([...materialState.componentsDepsMap.styles, ...(styles || [])])
+materialState.componentsDepsMap.styles = updatedStyles

Likely invalid or redundant comment.


323-366: ⚠️ Potential issue

Add validation for material bundle structure.

The parseMaterialsDependencies function should validate the material bundle structure before processing.

 const parseMaterialsDependencies = (materialBundle) => {
+  if (!materialBundle || typeof materialBundle !== 'object') {
+    console.error('Invalid material bundle:', materialBundle)
+    return
+  }
+
   const { packages, components } = materialBundle
 
   const { scripts: scriptsDeps, styles: stylesDeps } = useResource().appSchemaState.canvasDeps
 
   packages?.forEach((pkg) => {
+    if (!pkg?.package) {
+      console.warn('Invalid package in material bundle:', pkg)
+      return
+    }
     if (scriptsDeps.find((item) => item.package === pkg.package)) {
       return
     }

Likely invalid or redundant comment.

packages/plugins/materials/src/composable/useMaterial.js Outdated Show resolved Hide resolved
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: 4

♻️ Duplicate comments (3)
packages/plugins/materials/src/composable/useMaterial.js (3)

285-299: ⚠️ Potential issue

Ensure item.content is defined in getUtilsDependencies

In the getUtilsDependencies function, if item.content is undefined, accessing item.content.cdnLink can lead to a runtime error. Please add a check to confirm that item.content exists before accessing its properties.

Apply this diff to fix the issue:

return utils
  .filter((item) => item.type === 'npm' && item.content && item.content.cdnLink)
  .map((item) => {
    const { package: pkg, version, cdnLink } = item.content
    return {
      package: pkg,
      version,
      script: cdnLink
    }
  })

327-343: ⚠️ Potential issue

Add validation for pkg.package in parseMaterialsDependencies

In the packages?.forEach loop, if pkg.package is undefined, it could cause unexpected behavior when pushing to scriptsDeps. Please add a check to ensure pkg.package exists before proceeding.

Apply this diff:

packages?.forEach((pkg) => {
+  if (!pkg?.package) {
+    return
+  }
   if (scriptsDeps.find((item) => item.package === pkg.package)) {
     return
   }
   scriptsDeps.push(pkg)
   // Rest of the code...

492-492: ⚠️ Potential issue

Add error handling for canvasDispatch method

The canvasDispatch method might be undefined if canvasApi.value is not available, which can lead to runtime errors. Please add a conditional check before calling canvasDispatch.

Apply this diff:

- useCanvas().canvasApi.value?.canvasDispatch('updateDependencies', { detail: materialState.componentsDepsMap })
+ const canvasApi = useCanvas().canvasApi.value
+ if (canvasApi?.canvasDispatch) {
+   canvasApi.canvasDispatch('updateDependencies', { detail: materialState.componentsDepsMap })
+ } else {
+   console.error('Canvas API is not available')
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 775f6c9 and 49caefe.

📒 Files selected for processing (6)
  • packages/canvas/DesignCanvas/src/importMap.js (2 hunks)
  • packages/canvas/container/src/CanvasContainer.vue (2 hunks)
  • packages/common/js/preview.js (1 hunks)
  • packages/plugins/bridge/src/js/resource.js (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (8 hunks)
  • packages/plugins/materials/src/composable/useResource.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/common/js/preview.js
  • packages/plugins/materials/src/composable/useResource.js
  • packages/plugins/bridge/src/js/resource.js
  • packages/canvas/container/src/CanvasContainer.vue
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check

packages/plugins/materials/src/composable/useMaterial.js Outdated Show resolved Hide resolved
packages/plugins/materials/src/composable/useMaterial.js Outdated Show resolved Hide resolved
packages/canvas/DesignCanvas/src/importMap.js Show resolved Hide resolved
packages/canvas/DesignCanvas/src/importMap.js Outdated Show resolved Hide resolved
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 (3)
packages/canvas/common/src/utils.js (1)

114-123: Enhance error handling and cache management.

The function should handle potential errors from dynamicImportComponentLib and implement cache invalidation.

Apply this diff to improve error handling and caching:

 export const getComponents = async ({ package: pkg, script, components }) => {
   if (!pkg) return
 
+  try {
     const modules = await dynamicImportComponentLib({ pkg, script })
 
     Object.entries(components).forEach(([componentId, exportName]) => {
+      if (!modules[exportName]) {
+        console.warn(`Component ${exportName} not found in package ${pkg}`)
+        return
+      }
       if (!window.TinyLowcodeComponent[componentId]) {
         window.TinyLowcodeComponent[componentId] = modules[exportName]
       }
     })
+  } catch (error) {
+    console.error(`Failed to load components from package ${pkg}:`, error)
+    throw error
+  }
 }
packages/plugins/materials/src/composable/useMaterial.js (2)

360-364: Validate array before iteration.

The code assumes styles is an array without validation.

Apply this diff to add array validation:

-  if (Array.isArray(styles)) {
-    styles.forEach((item) => stylesDeps.add(item))
-  } else {
-    stylesDeps.add(styles)
-  }
+  if (!styles) return
+  
+  const stylesToAdd = Array.isArray(styles) ? styles : [styles]
+  stylesToAdd.forEach((item) => {
+    if (item) stylesDeps.add(item)
+  })

478-478: Improve array handling and reactivity.

The current implementation might have issues with array validation and reactive updates.

Apply this diff to improve array handling:

-  styles?.forEach((item) => materialState.componentsDepsMap.styles.add(item))
+  if (Array.isArray(styles)) {
+    styles.forEach((item) => {
+      if (item) materialState.componentsDepsMap.styles.add(item)
+    })
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1942594 and 8c36268.

📒 Files selected for processing (3)
  • packages/canvas/common/src/utils.js (1 hunks)
  • packages/canvas/render/src/runner.js (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/canvas/render/src/runner.js
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (4)
packages/canvas/common/src/utils.js (2)

85-88: 🛠️ Refactor suggestion

Add input validation for required parameters.

The function should validate the pkg parameter before proceeding.

Apply this diff to add input validation:

 const dynamicImportComponentLib = async ({ pkg, script }) => {
+  if (!pkg) {
+    throw new Error('Package name is required')
+  }
+
   if (window.TinyComponentLibs[pkg]) {
     return window.TinyComponentLibs[pkg]
   }

Likely invalid or redundant comment.


100-102: 🛠️ Refactor suggestion

Improve error handling in dynamic imports.

The current error handling silently fails by returning an empty object. This could mask critical issues.

Apply this diff to improve error handling:

   } catch (error) {
-    modules = {}
+    console.error(`Failed to load component library ${pkg}:`, error)
+    throw error
   }

Likely invalid or redundant comment.

packages/plugins/materials/src/composable/useMaterial.js (2)

322-324: 🛠️ Refactor suggestion

Add input validation for material bundle.

The function should validate the input object before processing.

Apply this diff to add input validation:

 const parseMaterialsDependencies = (materialBundle) => {
+  if (!materialBundle || typeof materialBundle !== 'object') {
+    console.warn('Invalid material bundle')
+    return
+  }
   const { packages, components } = materialBundle

Likely invalid or redundant comment.


288-298: 🛠️ Refactor suggestion

Add property validation in utility dependency parsing.

The function assumes properties exist without proper validation, which could lead to runtime errors.

Apply this diff to add property validation:

   return utils
-    .filter((item) => item.type === 'npm' && item.content.cdnLink)
+    .filter((item) => item.type === 'npm' && item.content?.cdnLink)
     .map((item) => {
+      if (!item.content?.package) {
+        console.warn('Invalid utility dependency: missing package name')
+        return null
+      }
       const { package: pkg, version, cdnLink } = item.content
 
       return {
         package: pkg,
         version,
         script: cdnLink
       }
-    })
+    })
+    .filter(Boolean)

Likely invalid or redundant comment.

packages/canvas/common/src/utils.js Outdated Show resolved Hide resolved
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: 3

♻️ Duplicate comments (2)
packages/canvas/common/src/utils.js (2)

73-73: ⚠️ Potential issue

Add fallback for imports property access.

The import map specification uses imports as the property name, but it might be undefined.

Apply this diff:

-    return Object.keys(importMapObject.imports)
+    return Object.keys(importMapObject.imports || {})

85-107: 🛠️ Refactor suggestion

Enhance error handling and input validation.

The current implementation needs improvements in several areas:

  1. Missing input parameter validation
  2. Silent error handling
  3. No timeout handling for dynamic imports

Apply these improvements:

-const dynamicImportComponentLib = async ({ pkg, script }) => {
+const dynamicImportComponentLib = async ({ pkg, script } = {}) => {
+  if (!pkg) {
+    throw new Error('Package name is required')
+  }
+
   if (window.TinyComponentLibs[pkg]) {
     return window.TinyComponentLibs[pkg]
   }

   let modules = {}

+  const importWithTimeout = (url) => {
+    const timeout = 30000 // 30 seconds
+    return Promise.race([
+      import(/* @vite-ignore */ url),
+      new Promise((_, reject) =>
+        setTimeout(() => reject(new Error(`Import timeout: ${url}`)), timeout)
+      )
+    ])
+  }

   try {
     const importMapKeys = getImportMapKeys()

     if (importMapKeys.includes(pkg)) {
-      modules = await import(/* @vite-ignore */ pkg)
+      modules = await importWithTimeout(pkg)
     } else if (script) {
-      modules = await import(/* @vite-ignore */ script)
+      modules = await importWithTimeout(script)
     }
   } catch (error) {
-    modules = {}
+    console.error(`Failed to load component library ${pkg}:`, error)
+    throw error
   }

   window.TinyComponentLibs[pkg] = modules
   return modules
 }
🧹 Nitpick comments (6)
packages/canvas/common/src/utils.js (3)

59-60: Enhance JSDoc documentation.

The current JSDoc could be more descriptive. Consider adding:

  • @returns {string[]} type information
  • Description of empty array return case
  • Example usage
 /**
- * 从页面importmap中获取模块的名称
- * @returns importmap中模块的名称集合
+ * Retrieves module names from the page's import map.
+ * @returns {string[]} Array of module names from import map, or empty array if import map is not found or invalid
+ * @example
+ * const moduleNames = getImportMapKeys();
+ * // Returns: ['vue', 'lodash', ...]
  */

79-84: Improve JSDoc documentation for dynamicImportComponentLib.

The current JSDoc lacks return type and parameter types.

 /**
  * 动态导入获取组件库模块
- * @param {*} pkg 模块名称
- * @param {*} script 模块的cdn地址
- * @returns
+ * @param {Object} options - The options object
+ * @param {string} options.pkg - Package name to import
+ * @param {string} [options.script] - Optional CDN URL for the package
+ * @returns {Promise<Object>} The imported module object
+ * @throws {Error} When package name is missing or import fails
  */

111-113: Enhance JSDoc documentation for getComponents.

The current JSDoc lacks proper parameter and return type definitions.

 /**
  * 获取组件对象并缓存,组件渲染时使用
- * @param {object} param0 组件的依赖: { package: 包名,script:js文件cdn, components:组件id和导出组件名的映射关系}
- * @returns
+ * @param {Object} options - The options object
+ * @param {string} options.package - Package name
+ * @param {string} [options.script] - Optional CDN URL for the package
+ * @param {Object.<string, string>} options.components - Mapping of component IDs to export names
+ * @returns {Promise<void>}
+ * @throws {Error} When required parameters are missing or component export is not found
  */
packages/canvas/render/src/RenderMain.js (1)

Line range hint 71-112: Enhance setUtils with validation and cleanup.

The function could benefit from input validation and more detailed error handling.

 const setUtils = async (data) => {
+  // Validate input
   if (!Array.isArray(data)) {
+    console.warn('setUtils: Invalid input data type')
     return
   }

+  // Track errors for comprehensive reporting
+  const errors = []
+
   // 筛选出来已经被删除的 key
   const newKeys = new Set(data.map(({ name }) => name))
   const currentKeys = Object.keys(utils)
   const deletedUtilsKeys = currentKeys.filter((item) => !newKeys.has(item))

   for (const key of deletedUtilsKeys) {
     delete utils[key]
   }

   const utilsCollection = {}

   data
     .filter((item) => item.type === 'function')
     .forEach((item) => {
+      try {
         const defaultFn = () => {}
         utilsCollection[item.name] = generateFunction(item.content.value, context) || defaultFn
+      } catch (error) {
+        errors.push(`Function ${item.name}: ${error.message}`)
+      }
     })

   const npmUtils = data.filter((item) => item.type === 'npm' && item.content.cdnLink)
   const results = await Promise.allSettled(npmUtils.map((item) => import(/* @vite-ignore */ item.content.cdnLink)))

   results.forEach((res, index) => {
     if (res.status !== 'fulfilled') {
+      const error = res.reason?.message || '未知错误'
       globalNotify({
         type: 'error',
-        message: `工具类 ${npmUtils[index].name} 加载失败,请检查CDN地址是否配置正确`
+        message: `工具类 ${npmUtils[index].name} 加载失败: ${error}`
       })
+      errors.push(`NPM ${npmUtils[index].name}: ${error}`)
       return
     }

     const module = res.value
     const { name, content } = npmUtils[index]
     const { exportName, destructuring } = content

     utilsCollection[name] = destructuring ? module[exportName] : module.default
   })

   Object.assign(utils, utilsCollection)

+  // Report all errors if any
+  if (errors.length > 0) {
+    console.error('setUtils encountered errors:', errors)
+  }

   // 因为工具类并不具有响应式行为,所以需要通过修改key来强制刷新画布
   refreshKey.value++
 }
packages/plugins/materials/src/composable/useMaterial.js (2)

281-288: Add validation in getCanvasDeps.

The function should validate the appSchemaState structure and filter invalid entries.

 const getCanvasDeps = () => {
+  const appSchemaState = useResource().appSchemaState
+  if (!appSchemaState?.materialsDeps) {
+    console.warn('Invalid appSchemaState structure')
+    return { scripts: [], styles: [] }
+  }
+
   const { scripts, styles } = useResource().appSchemaState.materialsDeps
 
   return {
-    scripts: [...scripts].filter((item) => item.script),
-    styles: [...styles]
+    scripts: [...(scripts || [])].filter((item) => item && item.script && typeof item.script === 'string'),
+    styles: [...(styles || [])].filter(Boolean)
   }
 }

432-433: Improve error handling in fetchMaterial.

The function should handle potential failures in updateCanvasDeps and consider the timing of the update.

-  updateCanvasDeps()
+  try {
+    await Promise.all(materials.map(async (response) => {
+      if (response.status === 'fulfilled' && response.value.materials) {
+        await addMaterials(response.value.materials)
+      }
+    }))
+    
+    updateCanvasDeps()
+  } catch (error) {
+    console.error('Failed to process materials:', error)
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c36268 and d8dd7eb.

📒 Files selected for processing (4)
  • packages/canvas/common/src/utils.js (1 hunks)
  • packages/canvas/render/src/RenderMain.js (2 hunks)
  • packages/plugins/bridge/src/js/resource.js (1 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (3)
packages/plugins/bridge/src/js/resource.js (3)

200-215: 🛠️ Refactor suggestion

Improve add operation and post-operation handling.

The add operation and post-operation tasks need better error handling and proper async/await usage.

-  const result = await requestAddReSource(data)
-
-  if (result) {
-    useResource().appSchemaState[data.category].push(result)
-  }
+  const result = await requestAddReSource(data)
+  if (!result) {
+    useNotify({
+      type: 'error',
+      message: '添加资源失败'
+    })
+    return
+  }
+  
+  useResource().appSchemaState[data.category].push(result)
 }

 // 更新画布工具函数环境,保证渲染最新工具类返回值, 并触发画布的强制刷新
-generateBridgeUtil(getAppId())
+try {
+  await generateBridgeUtil(getAppId())
+} catch (error) {
+  useNotify({
+    type: 'error',
+    message: `画布更新失败: ${error.message}`
+  })
+  return
+}

Likely invalid or redundant comment.


178-179: 🛠️ Refactor suggestion

Add top-level error handling.

The async function should have a try-catch wrapper to handle any unexpected errors that might occur during resource operations.

-export const saveResource = async (data, callback, emit) => {
+export const saveResource = async (data, callback, emit) => {
+  try {
     const isEdit = getActionType() === ACTION_TYPE.Edit
+    // ... rest of the function
+  } catch (error) {
+    useNotify({
+      type: 'error',
+      message: `操作失败: ${error.message}`
+    })
+    return
+  }
}

Likely invalid or redundant comment.


181-198: 🛠️ Refactor suggestion

Enhance error handling in edit operation.

The edit operation needs better error handling and validation of the update result.

 if (isEdit) {
   data.id = state.resource.id
-  const result = await requestUpdateReSource(data)
-
-  if (result) {
+  const result = await requestUpdateReSource(data)
+  if (!result) {
+    useNotify({
+      type: 'error',
+      message: '更新资源失败'
+    })
+    return
+  }
+
+  try {
     const index = useResource().appSchemaState[data.category].findIndex((item) => item.name === result.name)
-
     if (index === -1) {
-      useNotify({
-        type: 'error',
-        message: '修改失败'
-      })
-
-      return
+      throw new Error('找不到要更新的资源')
     }
-
     useResource().appSchemaState[data.category][index] = result
+  } catch (error) {
+    useNotify({
+      type: 'error',
+      message: `修改失败: ${error.message}`
+    })
+    return
   }
 }

Likely invalid or redundant comment.

@hexqi hexqi added this to the v2.2.0 milestone Jan 13, 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: 3

🧹 Nitpick comments (1)
packages/plugins/materials/src/composable/useResource.js (1)

36-37: Add JSDoc documentation for the new materialsDeps property.

While the structure is well-designed (using Set for styles to prevent duplicates), please add JSDoc documentation to describe the purpose and structure of the materialsDeps property.

+/**
+ * @typedef {Object} MaterialsDeps
+ * @property {Array<string>} scripts - Array of script URLs for material dependencies
+ * @property {Set<string>} styles - Set of style URLs for material dependencies
+ */
 const appSchemaState = reactive({
   dataSource: [],
   pageTree: [],
   langs: {},
   utils: {},
   globalState: [],
   materialsDeps: { scripts: [], styles: new Set() }
 })
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8dd7eb and b26bbf4.

📒 Files selected for processing (3)
  • packages/common/js/preview.js (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (7 hunks)
  • packages/plugins/materials/src/composable/useResource.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/common/js/preview.js
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (7)
packages/plugins/materials/src/composable/useMaterial.js (5)

22-23: LGTM! Good separation of concerns.

The new imports and state management changes improve the code organization by:

  • Separating message handling and resource management
  • Introducing a clear structure for component dependencies

Also applies to: 42-43


503-504: LGTM! Good documentation of exported functions.

The new exported functions are well-documented and maintain consistency with the existing exports.


457-457: ⚠️ Potential issue

Verify that styles is an array before iterating.

When using styles?.forEach(...), it's important to check that styles is an array to prevent potential runtime errors.

Apply this diff:

-  styles?.forEach((item) => materialState.componentsDepsMap.styles.add(item))
+  if (Array.isArray(styles)) {
+    styles.forEach((item) => materialState.componentsDepsMap.styles.add(item))
+  }

Likely invalid or redundant comment.


293-298: ⚠️ Potential issue

Add error handling for message publishing.

The updateCanvasDeps function should handle potential failures in message publishing.

Apply this diff to add proper error handling:

 const updateCanvasDeps = () => {
+  try {
+    const deps = getCanvasDeps()
+    if (!deps.scripts.length && !deps.styles.length) {
+      console.warn('No dependencies to update')
+      return
+    }
     useMessage().publish({
       topic: 'init_canvas_deps',
-      data: getCanvasDeps()
+      data: deps
     })
+  } catch (error) {
+    console.error('Failed to update canvas dependencies:', error)
+  }
 }

Likely invalid or redundant comment.


301-344: ⚠️ Potential issue

Add validation for material bundle structure.

The parseMaterialsDependencies function needs input validation and proper error handling.

Apply this diff to add proper validation:

 const parseMaterialsDependencies = (materialBundle) => {
+  if (!materialBundle || typeof materialBundle !== 'object') {
+    console.warn('Invalid materialBundle')
+    return
+  }
   const { packages, components } = materialBundle
+  if (!Array.isArray(packages) && !Array.isArray(components)) {
+    console.warn('No valid dependencies found in materialBundle')
+    return
+  }

   const { scripts: scriptsDeps, styles: stylesDeps } = useResource().appSchemaState.materialsDeps
+  if (!scriptsDeps || !stylesDeps) {
+    console.error('Invalid dependency state structure')
+    return
+  }

   packages?.forEach((pkg) => {
+    try {
       if (!pkg.script || scriptsDeps.find((item) => item.package === pkg.package)) {
         return
       }

       scriptsDeps.push(pkg)

       if (!pkg.css) {
         return
       }

       if (Array.isArray(pkg.css)) {
-        pkg.css.forEach((item) => stylesDeps.add(item))
+        pkg.css.filter(Boolean).forEach((item) => stylesDeps.add(item))
       } else {
         stylesDeps.add(pkg.css)
       }
+    } catch (error) {
+      console.error(`Error processing package ${pkg?.package}:`, error)
+    }
   })

Likely invalid or redundant comment.

packages/plugins/materials/src/composable/useResource.js (2)

192-192: LGTM!

The export of getUtilsDeps follows the existing pattern correctly.


36-37: Verify integration with canvas dependencies.

Please ensure that materialsDeps is properly initialized and consumed by the canvas. Let's verify the integration:

Also applies to: 178-187

✅ Verification successful

✓ Integration with canvas dependencies verified

The materialsDeps structure is properly initialized and consistently accessed across the codebase through useResource().appSchemaState. The canvas integration is correctly implemented in CanvasContainer.vue, maintaining the expected structure for scripts and styles.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how materialsDeps is used across the codebase

# Check for materialsDeps usage
echo "Checking materialsDeps usage:"
rg -l "materialsDeps" 

# Check for getUtilsDeps usage
echo -e "\nChecking getUtilsDeps usage:"
rg -l "getUtilsDeps"

# Look for potential initialization points
echo -e "\nChecking potential initialization points:"
ast-grep --pattern 'appSchemaState.materialsDeps = $_'

Length of output: 591


Script:

#!/bin/bash
# Check how materialsDeps is used in each file
echo "materialsDeps usage in CanvasContainer.vue:"
rg "materialsDeps" packages/canvas/container/src/CanvasContainer.vue -A 2

echo -e "\nmaterialsDeps usage in useMaterial.js:"
rg "materialsDeps" packages/plugins/materials/src/composable/useMaterial.js -A 2

echo -e "\nInitialization in useResource.js:"
rg "materialsDeps.*=|const.*materialsDeps" packages/plugins/materials/src/composable/useResource.js -A 2

Length of output: 923

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 (2)
packages/plugins/bridge/src/js/resource.js (2)

200-206: 🛠️ Refactor suggestion

Improve error handling in the create flow.

Similar to the edit flow, the create flow should have better error handling and input validation.

    } else {
      const result = await requestAddReSource(data)
-      if (result) {
-        useResource().appSchemaState[data.category].push(result)
+      if (!result) {
+        throw new Error('Add resource request failed')
       }
+      useResource().appSchemaState[data.category].push(result)
    }

208-216: 🛠️ Refactor suggestion

Handle async operations in success flow.

The canvas update and subsequent operations should handle potential async failures.

-    generateBridgeUtil(getAppId())
+    await generateBridgeUtil(getAppId())
     useNotify({
       type: 'success',
       message: `${isEdit ? '修改' : '创建'}成功`
     })
     emit('refresh', state.type)
     state.refresh = true
     callback()
🧹 Nitpick comments (1)
packages/plugins/bridge/src/js/resource.js (1)

217-221: Enhance error handling with specific error types and logging.

Consider adding:

  1. Error type classification for different failure scenarios
  2. Error logging for debugging
  3. Cleanup operations in case of failure
   } catch (error) {
+    console.error('Resource operation failed:', error)
     useNotify({
       type: 'error',
-      message: `工具类${isEdit ? '修改' : '创建'}失败:${error.message}`
+      message: `工具类${isEdit ? '修改' : '创建'}失败:${
+        error.code === 'NETWORK_ERROR' ? '网络错误' :
+        error.code === 'VALIDATION_ERROR' ? '验证失败' :
+        error.message
+      }`
     })
+    // Cleanup if needed
+    if (!isEdit && state.resource.id) {
+      try {
+        await requestDeleteReSource(`app=${getAppId()}&id=${state.resource.id}`)
+      } catch (cleanupError) {
+        console.error('Cleanup failed:', cleanupError)
+      }
+    }
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b26bbf4 and 6de0c1f.

📒 Files selected for processing (5)
  • packages/canvas/DesignCanvas/src/importMap.js (2 hunks)
  • packages/canvas/common/src/utils.js (1 hunks)
  • packages/plugins/bridge/src/js/resource.js (2 hunks)
  • packages/plugins/materials/src/composable/useMaterial.js (7 hunks)
  • packages/plugins/materials/src/composable/useResource.js (2 hunks)
🧰 Additional context used
📓 Learnings (1)
packages/canvas/DesignCanvas/src/importMap.js (1)
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.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (16)
packages/canvas/DesignCanvas/src/importMap.js (4)

3-3: LGTM! Function signature updated with proper defaults.

The new parameter canvasDeps is well-structured with appropriate default values for both scripts and styles.


48-49: LGTM! Dependencies are properly merged.

The import map correctly combines dependencies from multiple sources using the spread operator.


53-53: LGTM! Styles are properly combined.

The styles from both block requirements and canvas dependencies are correctly merged.


35-41: Verify script URLs before adding to import map.

The function processes external scripts but should validate URLs before adding them to the import map to prevent security issues.

Run this script to check for potentially unsafe URLs:

packages/canvas/common/src/utils.js (3)

62-77: LGTM! Import map keys are safely extracted.

The function includes proper error handling and null checks for the import map element.


115-124: 🛠️ Refactor suggestion

Add validation for component exports.

The function should validate that the exported components exist in the modules before caching.

Apply this diff to add validation:

 export const getComponents = async ({ package: pkg, script, components }) => {
   if (!pkg) return
 
   const modules = await dynamicImportComponentLib({ pkg, script })
 
   Object.entries(components).forEach(([componentId, exportName]) => {
+    if (!modules[exportName]) {
+      console.warn(`Component "${exportName}" not found in package "${pkg}"`)
+      return
+    }
     if (!window.TinyLowcodeComponent[componentId]) {
       window.TinyLowcodeComponent[componentId] = modules[exportName]
     }
   })
 }

Likely invalid or redundant comment.


85-107: 🛠️ Refactor suggestion

Add timeout handling for dynamic imports.

The function should handle potential timeouts during dynamic imports to prevent hanging.

Apply this diff to add timeout handling:

 const dynamicImportComponentLib = async ({ pkg, script }) => {
   if (window.TinyComponentLibs[pkg]) {
     return window.TinyComponentLibs[pkg]
   }

   let modules = {}
+  const importWithTimeout = (url) => {
+    return Promise.race([
+      import(/* @vite-ignore */ url),
+      new Promise((_, reject) =>
+        setTimeout(() => reject(new Error(`Import timeout: ${url}`)), 30000)
+      )
+    ])
+  }

   try {
     const importMapKeys = getImportMapKeys()

     if (importMapKeys.includes(pkg)) {
-      modules = await import(/* @vite-ignore */ pkg)
+      modules = await importWithTimeout(pkg)
     } else if (script) {
-      modules = await import(/* @vite-ignore */ script)
+      modules = await importWithTimeout(script)
     }
   } catch (error) {
+    console.error(`Failed to load module ${pkg}:`, error)
     modules = {}
   }

   window.TinyComponentLibs[pkg] = modules
   return modules
 }

Likely invalid or redundant comment.

packages/plugins/materials/src/composable/useResource.js (2)

36-37: LGTM! State management is properly structured.

The materialsDeps property is well-organized with separate arrays for scripts and styles.


179-187: 🛠️ Refactor suggestion

Add validation for utility dependencies.

The function should validate the structure of utility items before processing.

Apply this diff to add validation:

 const getUtilsDeps = () => {
+  if (!Array.isArray(appSchemaState.utils)) {
+    console.warn('Utils is not properly initialized')
+    return []
+  }
   return appSchemaState.utils.map((item) => {
+    if (!item?.content?.package || !item?.content?.cdnLink) {
+      console.warn(`Invalid utility structure:`, item)
+      return null
+    }
     return {
       ...item,
       package: item.content.package,
       script: item.content.cdnLink
     }
-  })
+  }).filter(Boolean)
 }

Likely invalid or redundant comment.

packages/plugins/materials/src/composable/useMaterial.js (5)

42-43: LGTM! Dependencies are properly organized.

The componentsDepsMap uses appropriate data structures with an array for scripts and a Set for styles to prevent duplicates.


281-288: 🛠️ Refactor suggestion

Add error handling for resource state access.

The function should handle cases where the material dependencies state is not properly initialized.

Apply this diff to add error handling:

 const getCanvasDeps = () => {
+  if (!useResource().appSchemaState?.materialsDeps) {
+    console.warn('Material dependencies not initialized')
+    return { scripts: [], styles: [] }
+  }
   const { scripts, styles } = useResource().appSchemaState.materialsDeps
 
   return {
     scripts: [...scripts].filter((item) => item.script),
     styles: [...styles]
   }
 }

Likely invalid or redundant comment.


293-298: 🛠️ Refactor suggestion

Add error handling for canvas updates.

The function should handle potential failures when publishing updates to the canvas.

Apply this diff to add error handling:

 const updateCanvasDeps = () => {
+  try {
+    const deps = getCanvasDeps()
+    if (!deps.scripts.length && !deps.styles.length) {
+      console.warn('No dependencies to update')
+      return
+    }
     useMessage().publish({
       topic: 'init_canvas_deps',
-      data: getCanvasDeps()
+      data: deps
     })
+  } catch (error) {
+    console.error('Failed to update canvas dependencies:', error)
+  }
 }

Likely invalid or redundant comment.


447-460: 🛠️ Refactor suggestion

Add validation for block dependencies.

The function should validate the structure of block dependencies before processing.

Apply this diff to add validation:

 scripts.forEach((npm) => {
+  if (!npm?.package) {
+    console.warn('Invalid package structure:', npm)
+    return
+  }
   const { package: pkg, script, css, components } = npm
   const npmInfo = materialState.componentsDepsMap.scripts.find((item) => item.package === pkg)
 
   if (!npmInfo || !npmInfo.script) {
     materialState.componentsDepsMap.scripts.push({ package: pkg, script, css, components })
   } else {
     const components = npmInfo.components || {}
     npmInfo.components = { ...components, ...npm.components }
   }
 })
 
-if (Array.isArray(styles)) {
+if (Array.isArray(styles) && styles.length > 0) {
   styles.forEach((item) => materialState.componentsDepsMap.styles.add(item))
 }

Likely invalid or redundant comment.


300-345: 🛠️ Refactor suggestion

Add validation for material bundle structure.

The function should validate the material bundle structure before processing dependencies.

Apply this diff to add validation:

 const parseMaterialsDependencies = (materialBundle) => {
+  if (!materialBundle || typeof materialBundle !== 'object') {
+    console.warn('Invalid material bundle')
+    return
+  }
   const { packages, components } = materialBundle
+  if (!Array.isArray(packages) && !Array.isArray(components)) {
+    console.warn('No valid dependencies found in material bundle')
+    return
+  }
 
   const { scripts: scriptsDeps, styles: stylesDeps } = useResource().appSchemaState.materialsDeps
 
   packages?.forEach((pkg) => {
+    if (!pkg?.package) {
+      console.warn('Invalid package entry:', pkg)
+      return
+    }
     if (!pkg.script || scriptsDeps.find((item) => item.package === pkg.package)) {
       return
     }
     // ... rest of the function
   })

Likely invalid or redundant comment.

packages/plugins/bridge/src/js/resource.js (2)

178-180: LGTM! Clean async/await conversion.

The function signature has been properly converted to async/await pattern, and the edit state check is cleanly extracted into a constant.


Line range hint 181-200: Enhance error handling in the edit flow.

The current implementation has nested conditions that make the code harder to follow. Consider restructuring for better error handling.

    if (isEdit) {
      data.id = state.resource.id
      const result = await requestUpdateReSource(data)
-      if (result) {
-        const index = useResource().appSchemaState[data.category].findIndex((item) => item.name === result.name)
-
-        if (index === -1) {
-          useNotify({
-            type: 'error',
-            message: '修改失败'
-          })
-
-          return
-        }
-
-        useResource().appSchemaState[data.category][index] = result
+      if (!result) {
+        throw new Error('Update resource request failed')
       }
+      
+      const index = useResource().appSchemaState[data.category].findIndex((item) => item.name === result.name)
+      if (index === -1) {
+        throw new Error('Resource not found in app schema')
+      }
+      
+      useResource().appSchemaState[data.category][index] = result

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

🔭 Outside diff range comments (1)
packages/design-core/src/preview/src/preview/Preview.vue (1)

Line range hint 164-177: Add input validation for code transformation.

The code transformation logic using transformSync could be vulnerable to code injection. Consider:

  1. Validating and sanitizing the input before transformation
  2. Adding content security policies
  3. Implementing rate limiting for transformations

Add input validation before code transformation:

+ const validateScript = (script) => {
+   // Add validation logic here
+   if (!script || typeof script !== 'string') {
+     throw new Error('Invalid script content');
+   }
+   // Add more validation as needed
+ }

  const transformedScript = transformSync(p1, {
+   validateScript(p1);
    babelrc: false,
    plugins: [[vueJsx, { pragma: 'h' }]],
    sourceMaps: false,
    configFile: false
  })
♻️ Duplicate comments (3)
packages/plugins/materials/src/composable/useResource.js (1)

185-196: 🛠️ Refactor suggestion

Add error handling and type documentation for getUtilsDeps.

The function needs defensive programming and better documentation.

+/**
+ * Transforms utils array into a format compatible with material dependencies.
+ * @returns {Array<{name: string, package: string, script: string}>} Array of utility dependencies
+ * @throws {Error} If utils array is not properly initialized
+ */
 const getUtilsDeps = () => {
+  if (!Array.isArray(appSchemaState.utils)) {
+    console.warn('Utils array is not properly initialized')
+    return []
+  }
   return appSchemaState.utils
     .filter((item) => item.type === 'npm')
     .map((item) => {
+      if (!item?.content) {
+        console.warn(`Missing content for util: ${item?.name}`)
+        return null
+      }
       return {
         ...item,
         package: item.content?.package,
         script: item.content?.cdnLink
       }
     })
+    .filter(Boolean)
 }
packages/canvas/common/src/utils.js (2)

59-71: 🛠️ Refactor suggestion

Add timeout handling and improve error reporting.

The dynamic import lacks timeout handling and proper error reporting.

Apply this improvement:

 const dynamicImportComponentLib = async ({ pkg, script }) => {
+  if (!pkg) {
+    throw new Error('Package name is required')
+  }
+
   if (window.TinyComponentLibs[pkg]) {
     return window.TinyComponentLibs[pkg]
   }

   if (!script) {
+    console.warn(`No script provided for package "${pkg}"`)
     return {}
   }

+  const importWithTimeout = (url) => {
+    const timeout = 30000 // 30 seconds
+    return Promise.race([
+      import(/* @vite-ignore */ url),
+      new Promise((_, reject) =>
+        setTimeout(() => reject(new Error(`Import timeout: ${url}`)), timeout)
+      )
+    ])
+  }

90-99: 🛠️ Refactor suggestion

Improve validation and error handling for component exports.

The function needs better parameter validation and error handling.

Apply this improvement:

 export const getComponents = async ({ package: pkg, script, components }) => {
-  if (!pkg) return
+  if (!pkg || !components || typeof components !== 'object') {
+    throw new Error('Package name and components mapping are required')
+  }

   const modules = await dynamicImportComponentLib({ pkg, script })

   Object.entries(components).forEach(([componentId, exportName]) => {
+    if (!modules[exportName]) {
+      console.warn(`Component export "${exportName}" not found in package "${pkg}"`)
+      return
+    }
     if (!window.TinyLowcodeComponent[componentId]) {
       window.TinyLowcodeComponent[componentId] = modules[exportName]
     }
   })
 }
🧹 Nitpick comments (3)
packages/plugins/materials/src/composable/useResource.js (1)

37-38: Document and optimize the materialsDeps property.

Consider the following improvements:

  1. Add JSDoc to document the purpose and structure of materialsDeps
  2. Based on the retrieved learning about component dependencies, consider making materialsDeps non-reactive if changes are handled explicitly
+/**
+ * @typedef {Object} MaterialsDeps
+ * @property {Array<string>} scripts - Array of script dependencies
+ * @property {Set<string>} styles - Set of unique style dependencies
+ */
 const appSchemaState = reactive({
   dataSource: [],
   pageTree: [],
   langs: {},
   utils: {},
   globalState: [],
-  materialsDeps: { scripts: [], styles: new Set() }
 })
+
+// Non-reactive material dependencies since changes are handled explicitly
+const materialsDeps = { scripts: [], styles: new Set() }
packages/canvas/common/src/utils.js (1)

86-88: Add JSDoc type annotations.

The JSDoc comment lacks type information for the parameters.

Improve the documentation:

 /**
  * 获取组件对象并缓存,组件渲染时使用
- * @param {object} param0 组件的依赖: { package: 包名,script:js文件cdn, components:组件id和导出组件名的映射关系}
+ * @param {object} param0 组件的依赖
+ * @param {string} param0.package 包名
+ * @param {string} param0.script js文件cdn地址
+ * @param {Record<string, string>} param0.components 组件id和导出组件名的映射关系
  * @returns {Promise<void>}
  */
packages/design-core/src/preview/src/preview/Preview.vue (1)

Line range hint 129-134: Consider improving error handling and documentation.

The block schema processing section contains TODs and complex logic that would benefit from:

  1. Proper error handling for failed block schema fetches
  2. Documentation explaining the block schema processing flow
  3. Resolution of the TODOs regarding cascade generation and built-in blocks

Consider extracting the block schema processing logic into a separate service to improve maintainability and testability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 861a6fc and 58a3376.

📒 Files selected for processing (8)
  • packages/canvas/DesignCanvas/src/DesignCanvas.vue (1 hunks)
  • packages/canvas/common/src/utils.js (2 hunks)
  • packages/canvas/container/src/CanvasContainer.vue (2 hunks)
  • packages/canvas/container/src/container.js (0 hunks)
  • packages/canvas/render/src/runner.ts (2 hunks)
  • packages/design-core/src/preview/src/preview/Preview.vue (1 hunks)
  • packages/plugins/materials/src/composable/useResource.js (2 hunks)
  • packages/plugins/materials/src/meta/block/src/BlockGroupPanel.vue (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/canvas/container/src/container.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/plugins/materials/src/meta/block/src/BlockGroupPanel.vue
  • packages/canvas/container/src/CanvasContainer.vue
🧰 Additional context used
📓 Learnings (1)
packages/plugins/materials/src/composable/useResource.js (1)
Learnt from: yy-wow
PR: opentiny/tiny-engine#940
File: packages/plugins/materials/src/composable/useMaterial.js:0-0
Timestamp: 2025-01-13T07:49:12.136Z
Learning: In the materials system, componentsDepsMap.scripts in materialState is used as a data store for tracking component dependencies and doesn't require Vue reactivity as changes are handled explicitly through updateCanvasDeps().
🔇 Additional comments (5)
packages/plugins/materials/src/composable/useResource.js (1)

201-201: LGTM!

The addition of getUtilsDeps to the exports is correct.

packages/canvas/render/src/runner.ts (1)

14-14: LGTM!

The import statement has been updated to reflect the new component loading strategy, removing unused imports.

packages/canvas/DesignCanvas/src/DesignCanvas.vue (2)

68-68: LGTM! Good use of Vue's reactivity system.

The change from a regular variable to a reactive reference using ref() is appropriate for Vue's reactivity system.


70-81: Add error handling for dependency initialization.

The message subscription implementation should handle potential errors during import map generation.

 useMessage().subscribe({
   topic: 'init_canvas_deps',
   callback: (deps) => {
     if (canvasSrc) {
       return
     }
+    try {
       const { importMap, importStyles } = getImportMapData(getMergeMeta('engine.config')?.importMapVersion, deps)
       canvasSrcDoc.value = initCanvas(importMap, importStyles).html
+    } catch (error) {
+      console.error('Failed to initialize canvas dependencies:', error)
+      // Consider showing a user-friendly error message
+    }
   }
 })
packages/design-core/src/preview/src/preview/Preview.vue (1)

126-126: Verify import map completeness and add validation.

The direct setting of import map data without utility processing could potentially miss some dependencies. Consider:

  1. Adding validation to ensure importMapData contains all required dependencies
  2. Documenting the expected structure of importMapData
  3. Adding error handling for malformed import maps

Let's verify if all required dependencies are properly included in the import map:

packages/canvas/render/src/runner.ts Outdated Show resolved Hide resolved
packages/canvas/DesignCanvas/src/DesignCanvas.vue Outdated Show resolved Hide resolved
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 (1)
packages/canvas/render/src/application-function/utils.ts (1)

62-77: Enhance error handling and type safety.

While the error handling is good, consider these improvements:

  1. Add more detailed error information
  2. Implement type checking for loaded modules
  3. Add debug logging
 npmUtils.forEach((res, index) => {
   const { name, content } = data[index]
   const { exportName, destructuring, cdnLink } = content

   if (res.status !== 'fulfilled') {
     globalNotify({
       type: 'error',
-      message: `加载工具类"${name}"失败,请检查cdn链接是否正确,${cdnLink}`
+      message: `Failed to load utility "${name}". Error: ${res.reason}. CDN: ${cdnLink}`
     })
     return
   }

   const module = res.value
+  // Type check the loaded module
+  if (destructuring && !(exportName in module)) {
+    globalNotify({
+      type: 'error',
+      message: `Export "${exportName}" not found in module "${name}"`
+    })
+    return
+  }
+
+  // Debug logging in development
+  if (process.env.NODE_ENV === 'development') {
+    console.debug(`Loaded utility "${name}" from ${cdnLink}`)
+  }

   utilsCollection[name] = destructuring ? module[exportName] : module.default
 })
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58a3376 and 6d22724.

📒 Files selected for processing (1)
  • packages/canvas/render/src/application-function/utils.ts (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (2)
packages/canvas/render/src/application-function/utils.ts (2)

5-5: LGTM! Import added for error notification.

The addition of globalNotify import is appropriate for handling error cases in the async utility loading.


22-22: Verify callers handle the async setUtils.

The function is now async, which is necessary for dynamic imports. Ensure all callers properly await this function.

✅ Verification successful

Watcher callback safely handles async setUtils

The only usage found is in a Vue watcher callback in RenderMain.ts. Vue's reactivity system automatically handles async callbacks in watchers, making this usage safe without explicit await. No changes needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for setUtils calls that might need updating
rg "setUtils\(" --type ts --type vue -B 2 -A 2

Length of output: 76


Script:

#!/bin/bash
# Search for setUtils calls in TypeScript files
rg "setUtils\(" --type ts -B 2 -A 2

# Search for potential method references and assignments
ast-grep --pattern 'setUtils($_)'

Length of output: 458


Script:

#!/bin/bash
# Get more context around the usage
rg "setUtils" packages/canvas/render/src/RenderMain.ts -B 10 -A 5

Length of output: 1276

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

♻️ Duplicate comments (1)
packages/canvas/DesignCanvas/src/DesignCanvas.vue (1)

70-82: ⚠️ Potential issue

Add error handling and subscription cleanup.

The subscription implementation needs improvements:

  1. No cleanup on component unmount
  2. Missing error handling for malformed deps
  3. Potential race conditions in initialization

Apply this diff to address the issues:

 export default {
   setup() {
     // ... other code ...
+    const subscription = ref(null)
     
-    useMessage().subscribe({
+    subscription.value = useMessage().subscribe({
       topic: 'init_canvas_deps',
       subscriber: 'canvas_design_canvas',
       callback: (deps) => {
+        // Protect against race conditions
+        if (canvasSrcDoc.value) {
+          return
+        }
+
+        // Validate deps parameter
+        if (!deps || typeof deps !== 'object') {
+          console.error('Invalid dependencies received')
+          return
+        }
+
         if (canvasSrc) {
           return
         }

         const { importMap, importStyles } = getImportMapData(getMergeMeta('engine.config')?.importMapVersion, deps)
         canvasSrcDoc.value = initCanvas(importMap, importStyles).html
       }
     })
+    
+    onUnmounted(() => {
+      if (subscription.value) {
+        useMessage().unsubscribe(subscription.value)
+      }
+    })
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d22724 and 16e762a.

📒 Files selected for processing (1)
  • packages/canvas/DesignCanvas/src/DesignCanvas.vue (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (1)
packages/canvas/DesignCanvas/src/DesignCanvas.vue (1)

68-68: LGTM! Good use of Vue 3 Composition API.

The change from a regular variable to a reactive reference using ref() is the correct approach.

packages/canvas/DesignCanvas/src/DesignCanvas.vue Outdated Show resolved Hide resolved
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

🧹 Nitpick comments (2)
packages/canvas/render/type.d.ts (2)

13-17: Add JSDoc documentation for the new property.

The new componentsDeps property lacks documentation explaining its purpose, usage, and relationship with existing properties like thirdPartyDeps.

Add JSDoc comments to improve maintainability:

+  /**
+   * Manages component dependencies and their relationships.
+   * Each entry defines a component package with its script source and component mappings.
+   * @example
+   * {
+   *   script: "https://unpkg.com/[email protected]/dist/index.js",
+   *   package: "my-component",
+   *   components: { "MyButton": "MyComponent.Button" }
+   * }
+   */
   componentsDeps: Array<{
     script: string
     package: string
     components: Record<string, string>
   }>

13-17: Consider improving type safety with more specific types.

The current type definition could be more specific to prevent potential runtime issues.

Consider these improvements:

   componentsDeps: Array<{
-    script: string
-    package: string
-    components: Record<string, string>
+    script: `https://${string}` | `http://${string}` | `//${string}`  // Ensure valid URLs
+    package: string  // Consider using PackageName type if available
+    components: {
+      [componentName: string]: `${string}.${string}` // Enforce dot notation for component paths
+    }
   }>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16e762a and 29fa9de.

📒 Files selected for processing (2)
  • packages/canvas/render/src/runner.ts (2 hunks)
  • packages/canvas/render/type.d.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/canvas/render/src/runner.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: push-check
🔇 Additional comments (1)
packages/canvas/render/type.d.ts (1)

Line range hint 4-17: Clarify the relationship between componentsDeps and thirdPartyDeps.

The interface now has two seemingly overlapping dependency management properties: thirdPartyDeps and componentsDeps. This could lead to confusion about which property should be used in different scenarios.

Let's analyze the usage to understand the distinction:

Consider:

  1. Can these properties be consolidated?
  2. Should we deprecate thirdPartyDeps in favor of componentsDeps?
  3. If both are needed, what's the clear distinction between their purposes?
✅ Verification successful

The distinction between thirdPartyDeps and componentsDeps is architecturally sound

  • thirdPartyDeps: Simple external resources (styles/scripts) without component mapping
  • componentsDeps: Component-specific dependencies that require package and component mapping information

These properties serve different purposes and should remain separate to maintain clean separation of concerns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage patterns of both properties
echo "=== thirdPartyDeps usage ==="
rg "thirdPartyDeps" -A 3

echo -e "\n=== componentsDeps usage ==="
rg "componentsDeps" -A 3

Length of output: 3316

@hexqi hexqi merged commit a7477a8 into opentiny:refactor/develop Jan 15, 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 ready_for_review This PR requires more reviews refactor-main refactor/develop branch feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants