IDEA Plugin - NPM Package Version History

· 3 min read · 588 Words · -Views -Comments

In IDEA or WebStorm, for package versions, we can only see the currently installed version or the latest version number, but there’s no built-in support for viewing historical versions. So I thought, why not create a plugin to enhance this functionality?

The Need for Viewing Package Version History

Viewing package version history has never been a critical requirement, but it’s still needed.

For example, when using webpack 4.x, I might want to know what versions exist beyond 4.x, because upgrades don’t always go to the latest version.

Another example: for packages we publish to private repositories, sometimes we need to know how many versions currently exist on the private server. Due to various reasons, some versions might no longer exist, and we need to verify.

And so on… the need exists, so all that’s left is to solve it.

npm | yarn Commands for Viewing Package Version History

npm or yarn commands themselves support querying available package version history

npm view webpack versions --json
yarn info webpack versions

Plugin Development Process Overview

Based on the above commands, the plugin essentially just wraps a visual UI around these commands. Let’s get started.

General Workflow

  1. Get the package name where the user’s cursor is focused
  2. Execute the command to get package version history
  3. Display the results in a popup list

I won’t go into the detailed development process here, just describe a few key points

Key Points

Controlling Menu Display and Availability State

My requirement is that the menu should only appear when the user right-clicks in a package.json file.

Method: Override the update method, Presentation provides methods to control this.

 @Override
    public void update(@NotNull AnActionEvent e) {
        PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
        String filename = psiFile.getVirtualFile().getName();
        Presentation presentation = e.getPresentation();
        if ("package.json".equals(filename)) {
            presentation.setEnabledAndVisible(true);
            return;
        }
        presentation.setEnabledAndVisible(false);
    }

Getting the Package Name

My requirement is to get the package name when the user right-clicks on the package name position

Method: PsiTreeUtil provides methods to get the direct parent element based on an element, thus obtaining the content

 PsiElement element = Objects.requireNonNull(PsiManager.getInstance(project).findFile(file)).findElementAt(offset);
        PsiElement firstParent = PsiTreeUtil.findFirstParent(element, new Condition<PsiElement>() {
            @Override
            public boolean value(PsiElement psiElement) {
                return true;
            }
        });
        assert firstParent != null;
        String packageName = getPackageName((LeafPsiElement) firstParent);

...
 @NotNull
    private String getPackageName(LeafPsiElement firstParent) {
        String name = firstParent.getText();
        return name.substring(1, name.length() - 1);
    }

Displaying Popup at Cursor Position


 private void showPopup(String name, List<String> versions, Editor editor) {
        IPopupChooserBuilder<String> popupChooserBuilder = JBPopupFactory.getInstance().createPopupChooserBuilder(versions);
        popupChooserBuilder.setTitle(name);
        JBPopup popup = popupChooserBuilder.createPopup();
        popup.setMinimumSize(new Dimension(80, 0));
        popup.showInBestPositionFor(editor);
    }

Executing Commands


 ArrayList<String> cmds = new ArrayList<>();
        cmds.add("npm");
        GeneralCommandLine generalCommandLine = new GeneralCommandLine(cmds);
        generalCommandLine.setCharset(Charset.forName("UTF-8"));
        generalCommandLine.setWorkDirectory(project.getBasePath());
        generalCommandLine.addParameters("view", name, "versions", "--json");
        String commandLineOutputStr = ScriptRunnerUtil.getProcessOutput(generalCommandLine);
        Gson converter = new Gson();
        Type type = new TypeToken<List<String>>() {
        }.getType();
        List<String> result = converter.fromJson(commandLineOutputStr, type);

Final Result

Plugin Store Address: Click here

Final Thoughts

  1. This should be the second IDEA plugin I’ve personally developed. The motivation was simple - existing IDEs and plugins don’t support this functionality, so I decided to develop it myself. Using plugins to fill the gaps in IDE capabilities helps improve personal productivity.
  2. The official documentation doesn’t provide examples for API methods, so relying solely on IDEA’s documentation isn’t sufficient. Multiple approaches are needed to solve problems during plugin development, such as posting on the official IDEA community forum, communicating with them on Slack, and examining the IDE and existing plugin SDKs. These can all help address current issues, so a diversified approach is necessary.

Reference Documents

Authors
Developer, digital product enthusiast, tinkerer, sharer, open source lover