UNPKG

@nx-dotnet/core

Version:

- Have an existing nx workspace. For creating this, see [nrwl's documentation](https://nx.dev/latest/angular/getting-started/nx-setup). - .NET SDK is installed, and `dotnet` is available on the path. For help on this, see [Microsoft's documentation](https

143 lines 5.79 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.checkModuleBoundariesForProject = checkModuleBoundariesForProject; exports.loadModuleBoundaries = loadModuleBoundaries; const devkit_1 = require("@nx/devkit"); const path_1 = require("path"); const utils_1 = require("@nx-dotnet/utils"); async function checkModuleBoundariesForProject(project, graph) { const projectRoot = graph.nodes[project].data.root; const tags = graph.nodes[project].data.tags ?? []; if (!tags.length) { return []; } const constraints = await getProjectConstraints(projectRoot, tags); if (!constraints.length) { return []; } const violations = []; (0, utils_1.forEachDependantProject)(project, graph, (configuration, name, implicit) => { if (implicit) return; const dependencyTags = configuration?.tags ?? []; for (const constraint of constraints) { if (hasConstraintViolation(constraint, dependencyTags)) { violations.push(`${project} cannot depend on ${name}. Project tag ${JSON.stringify(constraint)} is not satisfied.`); } } }); return violations; } async function getProjectConstraints(root, tags) { const configuredConstraints = await loadModuleBoundaries(root); return configuredConstraints.filter((x) => ((x.sourceTag && hasMatch(tags, x.sourceTag)) || x.allSourceTags?.every((tag) => hasMatch(tags, tag))) && (!x.onlyDependOnLibsWithTags?.includes('*') || x.notDependOnLibsWithTags?.length)); } function hasConstraintViolation(constraint, dependencyTags) { return (!dependencyTags.some((x) => hasMatch(constraint.onlyDependOnLibsWithTags ?? [], x)) || dependencyTags.some((x) => hasMatch(constraint.notDependOnLibsWithTags ?? [], x))); } /** * Loads module boundaries from eslintrc or .nx-dotnet.rc.json * @param root Which file should be used when pulling from eslint * @returns List of module boundaries */ async function loadModuleBoundaries(root, host) { const configured = (0, utils_1.readConfig)(host).moduleBoundaries; if (!configured) { try { // eslint-disable-next-line @typescript-eslint/no-var-requires const { ESLint } = require('eslint'); const result = await new ESLint() .calculateConfigForFile(`${root}/non-existant.ts`) .catch(() => Promise.resolve({ rules: { '@nx/enforce-module-boundaries': [] }, })); const [, moduleBoundaryConfig] = result.rules['@nx/enforce-module-boundaries'] || result.rules['@nrwl/nx/enforce-module-boundaries'] || []; return moduleBoundaryConfig?.depConstraints ?? []; } catch (e) { return []; } } else { return configured; } } function findProjectGivenRoot(root, graph) { // Note that this returns the first matching project and would succeed for multiple (cs|fs...)proj under an nx project path, // but getProjectFileForNxProject explicitly throws if it's not exactly one. const normalizedRoot = root.replace(/^["'](.+(?=["']$))["']$/, '$1'); const projectNode = Object.values(graph.nodes).find((projectConfig) => { const relativePath = (0, path_1.relative)(projectConfig.data.root, normalizedRoot); return relativePath?.startsWith('..') === false; }); const projectName = projectNode?.name; if (projectName) { return projectName; } else { console.error(`Failed to find nx workspace project associated with dotnet project directory: ${root}`); process.exit(1); } } const regexMap = new Map(); function hasMatch(input, pattern) { if (pattern === '*') return true; // if the pattern is a regex, check if any of the input strings match the regex if (pattern.startsWith('/') && pattern.endsWith('/')) { let regex = regexMap.get(pattern); if (!regex) { regex = new RegExp(pattern.substring(1, pattern.length - 1)); regexMap.set(pattern, regex); } return input.some((t) => regex?.test(t)); } // if the pattern is a glob, check if any of the input strings match the glob prefix if (pattern.includes('*')) { const regex = mapGlobToRegExp(pattern); return input.some((t) => regex.test(t)); } return input.indexOf(pattern) > -1; } /** * Maps import with wildcards to regex pattern * @param importDefinition * @returns */ function mapGlobToRegExp(importDefinition) { // we replace all instances of `*`, `**..*` and `.*` with `.*` const mappedWildcards = importDefinition.split(/(?:\.\*)|\*+/).join('.*'); return new RegExp(`^${new RegExp(mappedWildcards).source}$`); } async function main() { const parser = await Promise.resolve().then(() => require('yargs-parser')); const { project, projectRoot } = parser(process.argv.slice(2), { alias: { project: 'p', }, string: ['project', 'projectRoot'], }); const graph = await (0, devkit_1.createProjectGraphAsync)(); // Find the associated nx project for the msbuild project directory. const nxProject = project ?? findProjectGivenRoot(projectRoot, graph); console.log(`Checking module boundaries for ${nxProject}`); const violations = await checkModuleBoundariesForProject(nxProject, graph); if (violations.length) { violations.forEach((error) => { console.error(error); }); process.exit(1); } process.exit(0); } if (require.main === module) { process.chdir(devkit_1.workspaceRoot); main(); } //# sourceMappingURL=check-module-boundaries.js.map