UNPKG

@jnxplus/nx-maven

Version:

[![npm version](https://badge.fury.io/js/@jnxplus%2Fnx-maven.svg)](https://badge.fury.io/js/@jnxplus%2Fnx-maven)

377 lines 16.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getWorkspaceData = getWorkspaceData; exports.getNormalizedOptions = getNormalizedOptions; exports.getCachePath = getCachePath; exports.readWorkspaceDataCache = readWorkspaceDataCache; exports.writeWorkspaceDataToCache = writeWorkspaceDataToCache; exports.addProjects = addProjects; exports.getEffectiveVersion = getEffectiveVersion; exports.validateTargetInputs = validateTargetInputs; exports.getOutputDirLocalRepo = getOutputDirLocalRepo; exports.getTask = getTask; exports.ifOutputDirLocalRepoNotPresent = ifOutputDirLocalRepoNotPresent; const tslib_1 = require("tslib"); const common_1 = require("@jnxplus/common"); const xml_1 = require("@jnxplus/xml"); const devkit_1 = require("@nx/devkit"); const fs_1 = require("fs"); const cache_directory_1 = require("nx/src/utils/cache-directory"); const path_1 = require("path"); const utils_1 = require("../utils"); const file_hasher_1 = require("nx/src/hasher/file-hasher"); const workspace_context_1 = require("nx/src/utils/workspace-context"); let NORMALIZED_OPTIONS_CACHE = undefined; const WORKSPACE_DATA_PROMISE_CACHE = new Map(); function getWorkspaceData(opts) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const normalizedOpts = getNormalizedOptions(opts); const cachePath = yield getCachePath(normalizedOpts); // Check if we already have a promise for this cache path if (WORKSPACE_DATA_PROMISE_CACHE.has(cachePath)) { const cachedPromise = WORKSPACE_DATA_PROMISE_CACHE.get(cachePath); if (cachedPromise) { return cachedPromise; } } // Create and cache the promise const workspaceDataPromise = computeWorkspaceData(normalizedOpts, cachePath); WORKSPACE_DATA_PROMISE_CACHE.set(cachePath, workspaceDataPromise); try { return yield workspaceDataPromise; } catch (error) { // Remove failed promise from cache so it can be retried WORKSPACE_DATA_PROMISE_CACHE.delete(cachePath); throw error; } }); } function computeWorkspaceData(normalizedOpts, cachePath) { return tslib_1.__awaiter(this, void 0, void 0, function* () { // Check file system cache first let data = readWorkspaceDataCache(cachePath); if (data) { return data; } const mavenRootDirAbsolutePath = (0, path_1.join)(devkit_1.workspaceRoot, normalizedOpts.mavenRootDirectory); data = { mavenRootDirAbsolutePath, targetDefaults: getTargetDefaults(), localRepo: normalizedOpts.localRepoRelativePath, projects: {}, }; addProjects(data, normalizedOpts, normalizedOpts.mavenRootDirectory); // Store data in cache for future use writeWorkspaceDataToCache(cachePath, data); return data; }); } function getNormalizedOptions(opts) { if (!NORMALIZED_OPTIONS_CACHE) { const plugin = (0, utils_1.getPlugin)(); const mavenRootDirectory = (0, utils_1.getMavenRootDirectory)(plugin); NORMALIZED_OPTIONS_CACHE = { buildTargetName: (0, common_1.getBuildTargetName)(plugin), testTargetName: (0, common_1.getTestTargetName)(plugin), serveTargetName: (0, common_1.getServeTargetName)(plugin), integrationTestTargetName: (0, common_1.getIntegrationTestTargetName)(plugin), buildImageTargetName: (0, common_1.getBuildImageTargetName)(plugin), mavenRootDirectory, localRepoRelativePath: (0, utils_1.getLocalRepositoryPath)(opts, (0, path_1.join)(devkit_1.workspaceRoot, mavenRootDirectory)), graphOptions: { skipAggregatorProjectLinking: (0, utils_1.getSkipAggregatorProjectLinkingOption)(plugin), skipProjectWithoutProjectJson: (0, utils_1.getSkipProjectWithoutProjectJsonOption)(plugin), }, }; } return NORMALIZED_OPTIONS_CACHE; } /** * Generate an hash filename matching all pom.xml content * If one pom.xml change, hash is invalidated * * @param normalizedOptions */ function getCachePath(normalizedOptions) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const hash = (0, devkit_1.hashArray)([ yield (0, workspace_context_1.hashWithWorkspaceContext)(devkit_1.workspaceRoot, ['pom.xml', '**/pom.xml']), (0, file_hasher_1.hashObject)(normalizedOptions), ]); return (0, path_1.join)(cache_directory_1.workspaceDataDirectory, `maven-workspace-data-${hash}.hash`); }); } function readWorkspaceDataCache(cachePath) { return process.env['NX_CACHE_PROJECT_GRAPH'] !== 'false' && (0, fs_1.existsSync)(cachePath) ? (0, devkit_1.readJsonFile)(cachePath) : null; } function writeWorkspaceDataToCache(cachePath, results) { (0, devkit_1.writeJsonFile)(cachePath, results); } function addProjects(data, normalizedOpts, projectRelativePath, aggregatorProjectArtifactId) { var _a; const projectAbsolutePath = (0, path_1.join)(devkit_1.workspaceRoot, projectRelativePath); const pomXmlPath = (0, path_1.join)(projectAbsolutePath, 'pom.xml'); const pomXmlContent = (0, xml_1.readXml)(pomXmlPath); const artifactId = (0, utils_1.getArtifactId)(pomXmlContent); const projectJsonPath = (0, path_1.join)(projectAbsolutePath, 'project.json'); data.projects[artifactId] = { artifactId, groupId: (0, utils_1.getGroupId)(artifactId, pomXmlContent), version: (0, utils_1.getVersion)(artifactId, pomXmlContent), isRootProject: !aggregatorProjectArtifactId, isPomPackaging: isPomPackagingFunction(pomXmlContent), projectRoot: getProjectRoot(projectAbsolutePath), projectAbsolutePath, dependencies: getDependencyArtifactIds(pomXmlContent), profileDependencies: getProfileDependencyArtifactIds(pomXmlContent), pluginDependencies: getPluginDependencyArtifactIds(pomXmlContent), parentProjectArtifactId: getParentProjectName(pomXmlContent), aggregatorProjectArtifactId: aggregatorProjectArtifactId, properties: getProperties(pomXmlContent), skipProject: normalizedOpts.graphOptions.skipProjectWithoutProjectJson && !(0, fs_1.existsSync)(projectJsonPath), }; const moduleXmlElementArray = (_a = pomXmlContent === null || pomXmlContent === void 0 ? void 0 : pomXmlContent.childNamed('modules')) === null || _a === void 0 ? void 0 : _a.childrenNamed('module'); if (moduleXmlElementArray === undefined) { return; } for (const moduleXmlElement of moduleXmlElementArray) { const moduleRelativePath = (0, devkit_1.joinPathFragments)(projectRelativePath, moduleXmlElement.val.trim()); addProjects(data, normalizedOpts, moduleRelativePath, artifactId); } } function getProjectRoot(projectAbsolutePath) { let projectRoot = (0, devkit_1.normalizePath)((0, path_1.relative)(devkit_1.workspaceRoot, projectAbsolutePath)); // projectRoot should not be an empty string if (!projectRoot) { projectRoot = '.'; } return projectRoot; } function getParentProjectName(pomXmlContent) { var _a, _b; const parentXmlElement = pomXmlContent.childNamed('parent'); if (parentXmlElement === undefined) { return undefined; } const relativePath = (_a = parentXmlElement.childNamed('relativePath')) === null || _a === void 0 ? void 0 : _a.val; if (!relativePath) { return undefined; } return (_b = parentXmlElement.childNamed('artifactId')) === null || _b === void 0 ? void 0 : _b.val; } function getDependencyArtifactIds(pomXml) { const dependenciesXml = pomXml.childNamed('dependencies'); if (dependenciesXml === undefined) { return []; } return dependenciesXml .childrenNamed('dependency') .filter((dependencyXmlElement) => { var _a; const groupId = (_a = dependencyXmlElement.childNamed('groupId')) === null || _a === void 0 ? void 0 : _a.val; return (groupId !== 'org.springframework.boot' && groupId !== 'io.quarkus' && groupId !== 'io.micronaut'); }) .map((dependencyXmlElement) => { var _a; return (_a = dependencyXmlElement.childNamed('artifactId')) === null || _a === void 0 ? void 0 : _a.val; }); } function isPomPackagingFunction(pomXmlContent) { const packagingXml = pomXmlContent.childNamed('packaging'); if (packagingXml === undefined) { return false; } return packagingXml.val === 'pom'; } function getEffectiveVersion(project, workspaceData) { let newVersion = project.version; //1 if version is constant return it if (isConstantVersion(newVersion)) { return newVersion; } //2 try to calculate version from project properties newVersion = getVersionFromProperties(newVersion, project.properties); if (isConstantVersion(newVersion)) { return newVersion; } //3 try to calculate version from parent project // we just calculate the part we didn't calculate in step 2 newVersion = getVersionFromParentProject(newVersion, project.parentProjectArtifactId, workspaceData.projects); if (isConstantVersion(newVersion)) { return newVersion; } //4 Can't calculate version, maybe contains something like ${project.parent.version} // call help:evaluate to get version and add warning because help:evaluate took a lot of time devkit_1.logger.warn(`Can't calculate version ${newVersion} of project ${project.artifactId} without using mvn help:evaluate that take a lot of time. Please Open an issue to address this case.`); return (0, utils_1.getExpressionValue)('project.version', workspaceData.mavenRootDirAbsolutePath, project.artifactId); } function getVersionFromParentProject(newVersion, parentProjectArtifactId, projects) { if (!parentProjectArtifactId) { return newVersion; } const parentProject = projects[parentProjectArtifactId]; newVersion = getVersionFromProperties(newVersion, parentProject.properties); if (isConstantVersion(newVersion)) { return newVersion; } return getVersionFromParentProject(newVersion, parentProject.parentProjectArtifactId, projects); } function validateTargetInputs(targetName, file, inputs) { if ((inputs !== null && inputs !== void 0 ? inputs : []).some((element) => typeof element === 'string' && element === '{options.outputDirLocalRepo}')) { throw new Error(`"{options.outputDirLocalRepo}" is not allowed in target inputs. To make it works, remove it from "${targetName}" target in "${file}" file. If you have a valid use case, please open an issue.`); } } function getTargetDefaults() { var _a; const targetDefaults = []; const nxJsonPath = (0, path_1.join)(devkit_1.workspaceRoot, 'nx.json'); const nxJson = (0, devkit_1.readJsonFile)(nxJsonPath); if (nxJson.targetDefaults) { for (const [targetName, target] of Object.entries(nxJson.targetDefaults)) { validateTargetInputs(targetName, 'nx.json', target.inputs); if (((_a = target.outputs) !== null && _a !== void 0 ? _a : []).some((element) => element === '{options.outputDirLocalRepo}')) { targetDefaults.push(targetName); } } } return targetDefaults; } function isConstantVersion(version) { const index = version.indexOf('${'); if (index >= 0) { return false; } return true; } function getProperties(pomXmlContent) { //properties const propertiesXml = pomXmlContent.childNamed('properties'); const properties = []; if (propertiesXml === undefined) { return properties; } propertiesXml.eachChild((propertyXml) => { properties.push({ key: propertyXml.name, value: propertyXml.val }); }); return properties; } function getVersionFromProperties(version, properties) { if (properties.length === 0) { return version; } const versionExpressions = extractExpressions(version); if (versionExpressions.length === 0) { throw new Error(`Version ${version} is a constant`); } const commonProperties = properties.filter((p) => versionExpressions.includes(p.key)); if (commonProperties.length === 0) { return version; } let parsedVersion = version; for (const property of commonProperties) { parsedVersion = parsedVersion.replace('${' + property.key + '}', property.value); } if (version === parsedVersion) { throw new Error(`Code not working properly: version ${version} and parsedVersion ${parsedVersion} should not be the same`); } return parsedVersion; } function extractExpressions(version) { const expressionRegex = /\${([^${}]*)}/g; const expressions = []; let match; while ((match = expressionRegex.exec(version)) !== null) { expressions.push(match[1]); } const containsAnExpression = expressions.some((p) => p.indexOf('$') >= 0); if (containsAnExpression) { throw new Error(`Version ${version} not correctly parsed with regex ${expressionRegex}`); } return expressions; } function getProfileDependencyArtifactIds(pomXml) { let results = []; const profilesXml = pomXml.childNamed('profiles'); if (profilesXml === undefined) { return []; } const profileXmlArray = profilesXml.childrenNamed('profile'); for (const profileXml of profileXmlArray) { const dependenciesXml = profileXml.childNamed('dependencies'); if (dependenciesXml === undefined) { continue; } const profileDependencyArtifactIds = dependenciesXml .childrenNamed('dependency') .map((dependencyXmlElement) => { var _a; return (_a = dependencyXmlElement.childNamed('artifactId')) === null || _a === void 0 ? void 0 : _a.val; }); results = results.concat(profileDependencyArtifactIds); } return results; } function getPluginDependencyArtifactIds(pomXml) { let results = []; const buildXml = pomXml.childNamed('build'); if (buildXml === undefined) { return []; } results = results.concat(getPluginDependencyArtifactIdsFromPluginsTag(buildXml)); const pluginManagementXml = buildXml.childNamed('pluginManagement'); if (pluginManagementXml === undefined) { return results; } results = results.concat(getPluginDependencyArtifactIdsFromPluginsTag(pluginManagementXml)); return results; } function getPluginDependencyArtifactIdsFromPluginsTag(xmlElement) { let results = []; const pluginsXml = xmlElement.childNamed('plugins'); if (pluginsXml === undefined) { return []; } const pluginXmlArray = pluginsXml.childrenNamed('plugin'); for (const profileXml of pluginXmlArray) { const dependenciesXml = profileXml.childNamed('dependencies'); if (dependenciesXml === undefined) { continue; } const pluginDependencyArtifactIds = dependenciesXml .childrenNamed('dependency') .map((dependencyXmlElement) => { var _a; return (_a = dependencyXmlElement.childNamed('artifactId')) === null || _a === void 0 ? void 0 : _a.val; }); results = results.concat(pluginDependencyArtifactIds); } return results; } function getOutputDirLocalRepo(localRepositoryPath, groupId, artifactId, projectVersion) { return (0, path_1.join)(localRepositoryPath, `${groupId.replace(new RegExp(/\./, 'g'), '/')}/${artifactId}/${projectVersion}`); } function getTask(isRootProject) { if (isRootProject) { return 'install -N'; } return 'install'; } // eslint-disable-next-line @typescript-eslint/no-explicit-any function ifOutputDirLocalRepoNotPresent(options) { if (!options) { return true; } if ('outputDirLocalRepo' in options) { return false; } return true; } //# sourceMappingURL=graph-utils.js.map