Skip to content

[duplicate-code] Duplicate Code: Repeated defineAsyncComponent Factory Pattern in dynamic-importer.js #134

Description

@github-actions

Assignee: @copilot

Summary

In shell/utils/dynamic-importer.js, 10 import* functions all follow the exact same 7-line structure: validate the name argument, throw if missing, and return a defineAsyncComponent(() => import(...)) call. The only difference between each function is the webpack chunk name and the import path prefix. This is ~70 lines of structural copy-paste inside a single file.

Duplication Details

Pattern: Repeated defineAsyncComponent factory with name guard

  • Severity: Medium
  • Occurrences: 10 functions
  • Location: shell/utils/dynamic-importer.js (lines 7–87)

Duplicated structure (repeated 10 times with minor variation):

export function importXxx(name) {
  if (!name) {
    throw new Error('Name required');
  }

  return defineAsyncComponent(() => import(/* webpackChunkName: "xxx" */ `@shell/path/${name}`));
}

Affected functions (lines 7–87):

Function Chunk Name Import Path
importCloudCredential cloud-credential @shell/cloud-credential/
importMachineConfig machine-config @shell/machine-config/
importLogin login @shell/components/auth/login/
importChart chart @shell/chart/
importList list @shell/list/
importDetail detail @shell/detail/
importEdit edit @shell/edit/
importDialog dialog @shell/dialog/
importDrawer drawer @shell/components/Drawer/
importWindowComponent components @shell/components/Window/

Similarly, 7 resolve* functions (lines 117–143) all follow:

export function resolveXxx(key) {
  return require.resolve(`@shell/path/${ key }`);
}

Impact Analysis

  • Maintainability: Any changes to input validation (e.g., better error messages, type checks) must be applied in 10 places.
  • Bug Risk: Inconsistent whitespace between if (!name) and if ( !name ) already exists across the functions, suggesting drift.
  • Code Bloat: ~70 lines reducible to a factory pattern plus a lookup table.

Note: This file deliberately avoids ESLint and uses dynamic import() expressions. Webpack requires static string prefixes in template literals for code splitting to work correctly. Any refactoring must preserve the literal path prefix before the dynamic name segment — test with a production build to confirm chunk generation is not affected.

Refactoring Recommendations

  1. Factory function approach — Create an internal createImporter(chunkName, pathPrefix) helper:

    function createImporter(chunkName, pathPrefix) {
      return function(name) {
        if (!name) {
          throw new Error('Name required');
        }
        // Each call site must still use a literal prefix for webpack analysis
        return defineAsyncComponent(() => import(`@shell/${pathPrefix}/${name}`));
      };
    }

    ⚠️ Webpack magic comments and static path analysis may prevent fully generic factories. Validate with yarn build that chunks are named and split correctly.

  2. Alternatively, consolidate the name guard into a single utility and keep each function body minimal:

    function requireName(name) {
      if (!name) throw new Error('Name required');
    }
    
    export function importList(name) {
      requireName(name);
      return defineAsyncComponent(() => import(/* webpackChunkName: "list" */ `@shell/list/${name}`));
    }

    This removes 3 duplicated lines per function without touching the import expression.

Implementation Checklist

  • Review duplication findings
  • Confirm webpack chunk splitting is preserved after refactoring
  • Extract requireName guard (or equivalent) to remove guard duplication
  • Explore factory approach if webpack analysis allows it
  • Run yarn build and verify chunk names are correct
  • Verify no functionality broken

Analysis Metadata

  • Analyzed Files: 1 (shell/utils/dynamic-importer.js)
  • Detection Method: Structural code analysis
  • Commit: 3331497
  • Analysis Date: 2026-07-04T21:20:52Z

Generated by Duplicate Code Detector · ● 2.2M ·

  • expires on Jul 6, 2026, 9:24 PM UTC

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions