UNPKG

@nx/gradle

Version:

The Nx Plugin for Gradle allows Gradle tasks to be run through Nx

226 lines (225 loc) • 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.addBuildGradleFileNextToSettingsGradle = addBuildGradleFileNextToSettingsGradle; exports.extractNxPluginVersion = extractNxPluginVersion; exports.updateNxPluginVersion = updateNxPluginVersion; exports.addNxProjectGraphPlugin = addNxProjectGraphPlugin; const devkit_1 = require("@nx/devkit"); const versions_1 = require("../../utils/versions"); const path_1 = require("path"); const exec_gradle_1 = require("../../utils/exec-gradle"); const version_catalog_ast_utils_1 = require("../../utils/version-catalog-ast-utils"); /** * Adds a `build.gradle(.kts)` file next to each `settings.gradle(.kts)` file found in the workspace. * If the build.gradle file already exists, it reads its contents. */ async function addBuildGradleFileNextToSettingsGradle(tree) { const settingsGradleFiles = await (0, devkit_1.globAsync)(tree, [ '**/settings.gradle', '**/settings.gradle.kts', ]); return settingsGradleFiles.map((settingsGradlePath) => { return ensureBuildGradleFile(tree, settingsGradlePath); }); } /** * Determines the appropriate build.gradle file path based on the settings file * and ensures it exists in the tree. Returns the path and contents. */ function ensureBuildGradleFile(tree, settingsGradlePath) { const isKotlinDsl = settingsGradlePath.endsWith('.kts'); const buildGradleFile = (0, path_1.join)((0, path_1.dirname)(settingsGradlePath), isKotlinDsl ? 'build.gradle.kts' : 'build.gradle'); let content = ''; if (tree.exists(buildGradleFile)) { content = tree.read(buildGradleFile, 'utf-8'); } else { tree.write(buildGradleFile, content); } return { filePath: buildGradleFile, content }; } // a regex to get the version in build file in format `id "dev.nx.gradle.project-graph" version "x"` const regex = /(id\s*\(?["']dev\.nx\.gradle\.project-graph["']\)?\s*version\s*\(?["'])([^"']+)(["']\)?)/; /** * Extract gradle plugin version from build.gradle file */ async function extractNxPluginVersion(gradleFilePath, gradleContent) { const match = gradleContent.match(regex); let version = match ? match[2] : null; if (!version) { try { const gradlewFile = (0, exec_gradle_1.findGradlewFile)(gradleFilePath, devkit_1.workspaceRoot, undefined); const buildEnvironment = (await (0, exec_gradle_1.execGradleAsync)((0, path_1.join)(devkit_1.workspaceRoot, gradlewFile), [ 'buildEnvironment', '--quiet', ])).toString(); version = getPluginVersion(buildEnvironment); } catch (e) { } // Silently ignore error, fallback remains null } return version; } function getPluginVersion(dependencyTree) { const lines = dependencyTree.split('\n'); for (const line of lines) { // line is dev.nx.gradle.project-graph:dev.nx.gradle.project-graph.gradle.plugin:version const match = line.match(/dev\.nx\.gradle\.project-graph:dev\.nx\.gradle\.project-graph\.gradle\.plugin:([^\s\\]+)/); if (match) { return match[1]; // returns the version part } } return null; // not found } /** * Updates the plugin version in the given Gradle file content. */ function updateNxPluginVersion(content, newVersion) { if (regex.test(content)) { return content.replace(regex, `$1${newVersion}$3`); } else { devkit_1.logger.warn(`Please update plugin dev.nx.gradle.project-graph to ${newVersion}`); } return content; } /** * Ensures all build.gradle(.kts) files use the expected version of dev.nx.gradle.project-graph. */ async function addNxProjectGraphPlugin(tree, expectedVersion = versions_1.gradleProjectGraphVersion) { const files = await addBuildGradleFileNextToSettingsGradle(tree); for (const { filePath, content } of files) { await addNxProjectGraphPluginToBuildGradle(filePath, content, expectedVersion, tree); } } /** * Converts a version catalog alias to a Gradle accessor path. * In Gradle, dashes in aliases become dots in the accessor. * e.g., "nx-project-graph" -> "nx.project.graph" */ function aliasToAccessorPath(alias) { return alias.replace(/-/g, '.'); } /** * Finds a version catalog file near the given build.gradle file and returns * the plugin alias if the Nx project graph plugin is defined in it. * Searches in the following order: * 1. Standard Gradle location: gradle/libs.versions.toml relative to the build.gradle file * 2. Workspace root: gradle/libs.versions.toml at the workspace root * 3. Any libs.versions.toml in subdirectories of the build.gradle file */ async function findVersionCatalogPluginAlias(gradleFilePath, tree) { const gradleDir = (0, path_1.dirname)(gradleFilePath); // Check standard Gradle version catalog locations const possibleCatalogPaths = [ // Standard location relative to the build.gradle file (0, path_1.join)(gradleDir, 'gradle', 'libs.versions.toml'), // Workspace root location (common in monorepos) 'gradle/libs.versions.toml', ]; for (const catalogPath of possibleCatalogPaths) { if (tree.exists(catalogPath)) { const catalogContent = tree.read(catalogPath, 'utf-8'); if (catalogContent) { const alias = (0, version_catalog_ast_utils_1.getPluginAliasFromCatalogAst)(catalogContent, versions_1.gradleProjectGraphPluginName); if (alias) { return alias; } } } } // Fallback: search for any version catalog in subdirectories const versionCatalogFiles = await (0, devkit_1.globAsync)(tree, [ (0, path_1.join)(gradleDir, '**/libs.versions.toml'), ]); for (const versionCatalogPath of versionCatalogFiles) { const catalogContent = tree.read(versionCatalogPath, 'utf-8'); if (catalogContent) { const alias = (0, version_catalog_ast_utils_1.getPluginAliasFromCatalogAst)(catalogContent, versions_1.gradleProjectGraphPluginName); if (alias) { return alias; } } } return null; } /** * Adds or updates the Nx Project Graph plugin in the build.gradle(.kts) file. * Ensures the correct version and applies the plugin to all projects. * Returns the updated build.gradle content. */ async function addNxProjectGraphPluginToBuildGradle(gradleFilePath, buildGradleContent, expectedVersion = versions_1.gradleProjectGraphVersion, tree) { const isKotlinDsl = gradleFilePath.endsWith('.kts'); // Check if a version catalog exists with the plugin alias const pluginAlias = await findVersionCatalogPluginAlias(gradleFilePath, tree); const versionCatalogPluginAccessor = pluginAlias ? `libs.plugins.${aliasToAccessorPath(pluginAlias)}` : null; // Determine the plugin declaration syntax based on whether we have a version catalog const nxProjectGraphReportPlugin = versionCatalogPluginAccessor ? `alias(${versionCatalogPluginAccessor})` : isKotlinDsl ? `id("${versions_1.gradleProjectGraphPluginName}") version("${expectedVersion}")` : `id "${versions_1.gradleProjectGraphPluginName}" version "${expectedVersion}"`; // Check if the plugin is already included (directly or via version catalog alias) const hasPluginDirectly = buildGradleContent.includes(versions_1.gradleProjectGraphPluginName); const hasPluginViaAlias = versionCatalogPluginAccessor && buildGradleContent.includes(versionCatalogPluginAccessor); const hasPlugin = hasPluginDirectly || hasPluginViaAlias; // Helper to add plugin to plugins block function addPluginToPluginsBlock(content) { return content.replace(/plugins\s*\{/, `plugins {\n ${nxProjectGraphReportPlugin}`); } // Helper to add plugins block if missing function addPluginsBlock(content) { return `plugins {\n ${nxProjectGraphReportPlugin}\n}\n${content}`; } // Helper to add plugin application to allprojects function addPluginToAllProjects(content) { const applyPlugin = versionCatalogPluginAccessor ? `plugin(${versionCatalogPluginAccessor})` : isKotlinDsl ? `plugin("${versions_1.gradleProjectGraphPluginName}")` : `plugin("${versions_1.gradleProjectGraphPluginName}")`; return `${content}\nallprojects {\n apply {\n ${applyPlugin}\n }\n}`; } // Add to plugins block if plugin not already present if (buildGradleContent.includes('plugins {')) { if (hasPlugin) { // Plugin already exists - update version if needed (only for direct declarations) if (hasPluginDirectly) { const currentVersion = await extractNxPluginVersion(gradleFilePath, buildGradleContent); if (currentVersion && currentVersion !== expectedVersion) { buildGradleContent = updateNxPluginVersion(buildGradleContent, expectedVersion); } } } else { buildGradleContent = addPluginToPluginsBlock(buildGradleContent); } } else { buildGradleContent = addPluginsBlock(buildGradleContent); } // Skip allprojects handling if plugin was already correctly declared via version catalog alias // In that case, the plugin is already applied and doesn't need to be propagated via allprojects if (!hasPluginViaAlias) { const applyPluginPattern = versionCatalogPluginAccessor ? new RegExp(`\\s*plugin\\(${versionCatalogPluginAccessor.replace(/\./g, '\\.')}\\)`) : new RegExp(`\\s*plugin\\(["']${versions_1.gradleProjectGraphPluginName}["']\\)`); if (buildGradleContent.includes('allprojects {')) { if (!applyPluginPattern.test(buildGradleContent)) { const applyPlugin = versionCatalogPluginAccessor ? `plugin(${versionCatalogPluginAccessor})` : isKotlinDsl ? `plugin("${versions_1.gradleProjectGraphPluginName}")` : `plugin "${versions_1.gradleProjectGraphPluginName}"`; buildGradleContent = buildGradleContent.replace(/allprojects\s*\{/, `allprojects {\n apply ${applyPlugin}`); } } else { buildGradleContent = addPluginToAllProjects(buildGradleContent); } } tree.write(gradleFilePath, buildGradleContent); return buildGradleContent; }