@nx/gradle
Version:
226 lines (225 loc) • 10.6 kB
JavaScript
;
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;
}