/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.gradle.plugins.node.tasks; import com.liferay.gradle.plugins.node.internal.util.FileUtil; import com.liferay.gradle.plugins.node.internal.util.GradleUtil; import com.liferay.gradle.util.Validator; import groovy.json.JsonSlurper; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.logging.Logger; import org.gradle.api.plugins.PluginContainer; import org.gradle.api.specs.Spec; /** * @author Andrea Di Giorgi */ public class NpmInstallTask extends ExecuteNpmTask { public NpmInstallTask() { _removeShrinkwrappedUrls = new Callable<Boolean>() { @Override public Boolean call() throws Exception { if (Validator.isNotNull(getRegistry())) { return true; } return false; } }; onlyIf( new Spec<Task>() { @Override public boolean isSatisfiedBy(Task task) { NpmInstallTask npmInstallTask = (NpmInstallTask)task; File packageJsonFile = npmInstallTask.getPackageJsonFile(); if (!packageJsonFile.exists()) { return false; } JsonSlurper jsonSlurper = new JsonSlurper(); Map<String, Object> packageJsonMap = (Map<String, Object>)jsonSlurper.parse(packageJsonFile); if (packageJsonMap.containsKey("dependencies") || packageJsonMap.containsKey("devDependencies")) { return true; } return false; } }); } @Override public void executeNode() throws Exception { executeNpmInstall(false); } public File getNodeModulesCacheDir() { return GradleUtil.toFile(getProject(), _nodeModulesCacheDir); } public File getNodeModulesDir() { Project project = getProject(); return project.file("node_modules"); } public File getPackageJsonFile() { Project project = getProject(); return project.file("package.json"); } public File getShrinkwrapJsonFile() { Project project = getProject(); File shrinkwrapJsonFile = project.file("npm-shrinkwrap.json"); if (!shrinkwrapJsonFile.exists()) { shrinkwrapJsonFile = null; } return shrinkwrapJsonFile; } public boolean isNodeModulesCacheNativeSync() { return _nodeModulesCacheNativeSync; } public boolean isNodeModulesCacheRemoveBinDirs() { return _nodeModulesCacheRemoveBinDirs; } public boolean isRemoveShrinkwrappedUrls() { return GradleUtil.toBoolean(_removeShrinkwrappedUrls); } public void setNodeModulesCacheDir(Object nodeModulesCacheDir) { _nodeModulesCacheDir = nodeModulesCacheDir; } public void setNodeModulesCacheNativeSync( boolean nodeModulesCacheNativeSync) { _nodeModulesCacheNativeSync = nodeModulesCacheNativeSync; } public void setNodeModulesCacheRemoveBinDirs( boolean nodeModulesCacheRemoveBinDirs) { _nodeModulesCacheRemoveBinDirs = nodeModulesCacheRemoveBinDirs; } /** * @deprecated As of 1.3.0, replaced by {@link * #setRemoveShrinkwrappedUrls(Object)} */ @Deprecated public void setRemoveShrinkwrappedUrls(boolean removeShrinkwrappedUrls) { _removeShrinkwrappedUrls = removeShrinkwrappedUrls; } public void setRemoveShrinkwrappedUrls(Object removeShrinkwrappedUrls) { _removeShrinkwrappedUrls = removeShrinkwrappedUrls; } protected void executeNpmInstall(boolean reset) throws Exception { Logger logger = getLogger(); Path shrinkwrapJsonBackupPath = null; Path shrinkwrapJsonPath = null; File shrinkwrapJsonFile = getShrinkwrapJsonFile(); if (isRemoveShrinkwrappedUrls() && (shrinkwrapJsonFile != null)) { shrinkwrapJsonPath = shrinkwrapJsonFile.toPath(); shrinkwrapJsonBackupPath = Paths.get( shrinkwrapJsonPath.toString() + ".backup"); Files.copy( shrinkwrapJsonPath, shrinkwrapJsonBackupPath, StandardCopyOption.REPLACE_EXISTING); _removeShrinkwrappedUrls(); } try { if (_isCacheEnabled()) { if (logger.isInfoEnabled()) { logger.info("Cache for {} is enabled", this); } _npmInstallCached(this, reset); } else { if (logger.isInfoEnabled()) { logger.info("Cache for {} is disabled", this); } _npmInstall(reset); } } finally { if (shrinkwrapJsonBackupPath != null) { Files.move( shrinkwrapJsonBackupPath, shrinkwrapJsonPath, StandardCopyOption.REPLACE_EXISTING); } } } @Override protected List<String> getCompleteArgs() { List<String> completeArgs = super.getCompleteArgs(); completeArgs.add("install"); return completeArgs; } private static String _getNodeModulesCacheDigest( NpmInstallTask npmInstallTask) { JsonSlurper jsonSlurper = new JsonSlurper(); File jsonFile = npmInstallTask.getShrinkwrapJsonFile(); if (jsonFile == null) { Logger logger = npmInstallTask.getLogger(); if (logger.isWarnEnabled()) { logger.warn( "Unable to find npm-shrinkwrap.json for {}, using " + "package.json instead", npmInstallTask.getProject()); } jsonFile = npmInstallTask.getPackageJsonFile(); } Map<String, Object> map = (Map<String, Object>)jsonSlurper.parse( jsonFile); map.remove("name"); map.remove("version"); return String.valueOf(map.hashCode()); } private static synchronized void _npmInstallCached( NpmInstallTask npmInstallTask, boolean reset) throws Exception { Logger logger = npmInstallTask.getLogger(); Project project = npmInstallTask.getProject(); String digest = _getNodeModulesCacheDigest(npmInstallTask); File nodeModulesCacheDir = new File( npmInstallTask.getNodeModulesCacheDir(), digest); File nodeModulesDir = npmInstallTask.getNodeModulesDir(); boolean nativeSync = npmInstallTask.isNodeModulesCacheNativeSync(); boolean removeBinDirs = npmInstallTask.isNodeModulesCacheRemoveBinDirs(); if (reset) { project.delete(nodeModulesCacheDir); } if (nodeModulesCacheDir.exists()) { if (logger.isLifecycleEnabled()) { logger.lifecycle( "Restoring node_modules of {} from {}", project, nodeModulesCacheDir); } FileUtil.syncDir( project, nodeModulesCacheDir, nodeModulesDir, nativeSync); if (removeBinDirs) { FileUtil.removeDirs( project, nodeModulesDir, _NODE_MODULES_BIN_DIR_NAME); } } else { npmInstallTask._npmInstall(reset); if (removeBinDirs) { FileUtil.removeDirs( project, nodeModulesDir, _NODE_MODULES_BIN_DIR_NAME); } if (logger.isLifecycleEnabled()) { logger.lifecycle( "Caching node_modules of {} in {}", project, nodeModulesCacheDir); } FileUtil.syncDir( project, nodeModulesDir, nodeModulesCacheDir, nativeSync); } } private boolean _isCacheEnabled() { Project project = getProject(); PluginContainer pluginContainer = project.getPlugins(); if (!pluginContainer.hasPlugin("com.liferay.cache") && (getNodeModulesCacheDir() != null)) { return true; } return false; } private void _npmInstall(boolean reset) throws Exception { Logger logger = getLogger(); int npmInstallRetries = getNpmInstallRetries(); Project project = getProject(); for (int i = 0; i < (npmInstallRetries + 1); i++) { if (reset || (i > 0)) { project.delete(getNodeModulesDir()); } try { super.executeNode(); break; } catch (IOException ioe) { if (i == npmInstallRetries) { throw ioe; } if (logger.isWarnEnabled()) { logger.warn( ioe.getMessage() + ". Running \"npm install\" again"); } } } } private void _removeShrinkwrappedUrls() throws IOException { File shrinkwrapJsonFile = getShrinkwrapJsonFile(); Path shrinkwrapJsonPath = shrinkwrapJsonFile.toPath(); String json = new String( Files.readAllBytes(shrinkwrapJsonPath), StandardCharsets.UTF_8); json = json.replaceAll( "\\s+\"(?:from|resolved)\": \"http.+\",*\\r*\\n", ""); Files.write(shrinkwrapJsonPath, json.getBytes(StandardCharsets.UTF_8)); } private static final String _NODE_MODULES_BIN_DIR_NAME = ".bin"; private Object _nodeModulesCacheDir; private boolean _nodeModulesCacheNativeSync = true; private boolean _nodeModulesCacheRemoveBinDirs = true; private Object _removeShrinkwrappedUrls; }