public synchronized File buildPackage(HeliumPackage pkg,
                                        boolean rebuild,
                                        boolean recopyLocalModule) throws IOException {
    if (pkg == null) {
      return null;
    }

    String[] moduleNameVersion = getNpmModuleNameAndVersion(pkg);
    if (moduleNameVersion == null) {
      LOGGER.warn("Can't get module name and version of package {}", pkg.getName());
      return null;
    }

    String pkgName = pkg.getName();
    File bundleDir = getHeliumPackageDirectory(pkgName);
    File bundleCache = getHeliumPackageBundleCache(pkgName);

    if (!rebuild && bundleCache.exists() && !bundleCache.isDirectory()) {
      return bundleCache;
    }

    // 0. install node, npm (should be called before `downloadPackage`
    try {
      installNodeAndNpm();
    } catch (TaskRunnerException e) {
      throw new IOException(e);
    }

    // 1. prepare directories
    if (!heliumLocalRepoDirectory.exists() || !heliumLocalRepoDirectory.isDirectory()) {
      FileUtils.deleteQuietly(heliumLocalRepoDirectory);
      FileUtils.forceMkdir(heliumLocalRepoDirectory);
    }
    FrontendPluginFactory fpf = new FrontendPluginFactory(
            bundleDir, nodeInstallationDirectory);

    // resources: webpack.js, package.json
    String templateWebpackConfig = Resources.toString(
        Resources.getResource("helium/webpack.config.js"), StandardCharsets.UTF_8);
    String templatePackageJson = Resources.toString(
        Resources.getResource("helium/" + PACKAGE_JSON), StandardCharsets.UTF_8);

    // 2. download helium package using `npm pack`
    String mainFileName = null;
    try {
      mainFileName = downloadPackage(pkg, moduleNameVersion, bundleDir,
              templateWebpackConfig, templatePackageJson, fpf);
    } catch (TaskRunnerException e) {
      throw new IOException(e);
    }

    // 3. prepare bundle source
    prepareSource(pkg, moduleNameVersion, mainFileName);

    // 4. install node and local modules for a bundle
    copyFrameworkModulesToInstallPath(recopyLocalModule); // should copy local modules first
    installNodeModules(fpf);

    // 5. let's bundle and update cache
    File heliumBundle = bundleHeliumPackage(fpf, bundleDir);
    bundleCache.delete();
    FileUtils.moveFile(heliumBundle, bundleCache);

    return bundleCache;
  }
@Test
  void switchVersion() throws IOException, TaskRunnerException {
    URL res = Resources.getResource("helium/webpack.config.js");
    String resDir = new File(res.getFile()).getParent();

    HeliumPackage pkgV1 =
        newHeliumPackage(
            HeliumType.VISUALIZATION,
            "zeppelin-bubblechart",
            "zeppelin-bubblechart",
            "zeppelin-bubblechart@0.0.3",
            "",
            null,
            "license",
            "icon");

    HeliumPackage pkgV2 =
        newHeliumPackage(
            HeliumType.VISUALIZATION,
            "zeppelin-bubblechart",
            "zeppelin-bubblechart",
            "zeppelin-bubblechart@0.0.1",
            "",
            null,
            "license",
            "icon");
    List<HeliumPackage> pkgsV1 = new LinkedList<>();
    pkgsV1.add(pkgV1);

    List<HeliumPackage> pkgsV2 = new LinkedList<>();
    pkgsV2.add(pkgV2);

    File bundle1 = hbf.buildPackage(pkgV1, true, true);
    File bundle2 = hbf.buildPackage(pkgV2, true, true);

    assertNotSame(bundle1.lastModified(), bundle2.lastModified());
  }