@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
JavaScript
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
;