Repository: facebook/docusaurus
File: packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx (TypeScript)

Code before:
} from '@docusaurus/plugin-content-docs/client';

type VersionConfiguration = {name: string} & PropVersionItem;

function getVersionConfigurations(
  versions: PropVersions,
): VersionConfiguration[] {
  if (Array.isArray(versions)) {
    return versions.map((name): VersionConfiguration => {
      return {name};
    });
  } else {
    return Object.entries(versions).map(
      ([name, version]): VersionConfiguration => {
        return {name, ...version};
      },
    );
  }
}

type ConfiguredVersion = {
  version: GlobalVersion;
  configuration?: VersionConfiguration;
};

function configureVersions(
  versions: GlobalVersion[],
  staticVersions?: PropVersions,
): ConfiguredVersion[] {
  if (staticVersions) {
    // The versions are configured

    // Collect all the versions we have
    const versionMap = new Map<string, GlobalVersion>(
      versions.map((version) => [version.name, version]),
    );

    // Add secondary version identifiers to make the configuration process
    // more natural to a user
    for (const version of versions) {
      // 'version.name' has special conventions for current and next versions,
      // that's why we use 'version.label' as a secondary version identifier
      // that can be referenced in configuration
      const label = version.label;
      if (!versionMap.has(label)) versionMap.set(label, version);
    }

    // Keep only versions specified in configuration, reorder them accordingly
    const configuredVersions: ConfiguredVersion[] = [];
    for (const configuration of getVersionConfigurations(staticVersions)) {
      const version = versionMap.get(configuration.name);

Review comment (bug):
Hmmm that looks a bit complicated to me.

Here's some pseudo-code I'd write to normalize the 3 cases under a single one:

```ts
type VersionConfig = {
  label?: string
}

type VersionItem = {
  version: GlobalVersion,
  config?: VersionConfig
}

function getDropdownVersions({versions,item)}): VersionItem[] {
  const versionMap = new Map<string, GlobalVersion>(
    versions.map((version) => [version.name, version]),
  );

  if ( item.versions ) {
    // Maybe fail-fast if the user provide version names that do not exist
    // But it's preferable to do this earlier in Node.js side, if possible
 
    if (Array.isArray(item.versions)) {
      return item.versions.map(name => ({
        version: versionMap[name],
        config: undefined;
      }));
    }
    else {
      // pseudo code: in practice, we don't want to import Lodash in theme code
      return _.mapValues(item.versions,(config,name) => ({
        version: versionMap[name],
        config,
      }));
    }
  }
  else {
    return versions.map(v => ({version, config: undefined})
  }
}
```


Code after:
} from '@docusaurus/plugin-content-docs/client';

type VersionItem = {
  version: GlobalVersion;
  label: string;
};

function getVersionItems(
  versions: GlobalVersion[],
  configs?: PropVersions,
): VersionItem[] {
  if (configs) {
    // Collect all the versions we have
    const versionMap = new Map<string, GlobalVersion>(
      versions.map((version) => [version.name, version]),
    );

    const toVersionItem = (
      name: string,
      config?: PropVersionItem,
    ): VersionItem => {
      const version = versionMap.get(name);
      if (!version) {
        throw new Error(`No docs version exist for name '${name}', please verify your 'docsVersionDropdown' navbar item versions config.
Available version names:\n- ${versions.map((v) => `${v.name}`).join('\n- ')}`);
      }
      return {version, label: config?.label ?? version.label};
    };

    if (Array.isArray(configs)) {
      return configs.map((name) => toVersionItem(name, undefined));
    } else {
      return Object.entries(configs).map(([name, config]) =>
        toVersionItem(name, config),
      );
    }
  } else {
    return versions.map((version) => ({version, label: version.label}));
  }
}

function useVersionItems({
  docsPluginId,
  configs,
}: {
  docsPluginId: Props['docsPluginId'];
  configs: Props['versions'];
}): VersionItem[] {
  const versions = useVersions(docsPluginId);
  return getVersionItems(versions, configs);
}