@nx/dotnet 0.0.1 → 22.0.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +117 -4
  2. package/README.md__tmpl__ +70 -0
  3. package/dist/analyzer/analyzer-client.d.ts +39 -0
  4. package/dist/analyzer/analyzer-client.d.ts.map +1 -0
  5. package/dist/analyzer/analyzer-client.js +227 -0
  6. package/dist/generators/init/init.d.ts +7 -0
  7. package/dist/generators/init/init.d.ts.map +1 -0
  8. package/dist/generators/init/init.js +107 -0
  9. package/dist/generators/init/schema.d.ts +6 -0
  10. package/dist/generators/init/schema.json +34 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +4 -0
  14. package/dist/lib/Microsoft.Build.Locator.dll +0 -0
  15. package/dist/lib/MsbuildAnalyzer +0 -0
  16. package/dist/lib/MsbuildAnalyzer.deps.json +476 -0
  17. package/dist/lib/MsbuildAnalyzer.dll +0 -0
  18. package/dist/lib/MsbuildAnalyzer.dll.config +629 -0
  19. package/dist/lib/MsbuildAnalyzer.pdb +0 -0
  20. package/dist/lib/MsbuildAnalyzer.runtimeconfig.json +14 -0
  21. package/dist/plugin.d.ts +1 -1
  22. package/dist/plugin.d.ts.map +1 -1
  23. package/dist/plugin.js +3 -2
  24. package/dist/plugins/create-dependencies.d.ts +4 -0
  25. package/dist/plugins/create-dependencies.d.ts.map +1 -0
  26. package/dist/plugins/create-dependencies.js +47 -0
  27. package/dist/{src/plugins/plugin.d.ts → plugins/create-nodes.d.ts} +2 -3
  28. package/dist/plugins/create-nodes.d.ts.map +1 -0
  29. package/dist/plugins/create-nodes.js +48 -0
  30. package/dist/plugins/plugin.d.ts +5 -0
  31. package/dist/plugins/plugin.d.ts.map +1 -0
  32. package/dist/plugins/plugin.js +14 -0
  33. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  34. package/dist/utils/cache.d.ts.map +1 -0
  35. package/dist/utils/has-dotnet-plugin.d.ts +3 -0
  36. package/dist/utils/has-dotnet-plugin.d.ts.map +1 -0
  37. package/dist/utils/has-dotnet-plugin.js +8 -0
  38. package/dist/utils/versions.d.ts +2 -0
  39. package/dist/utils/versions.d.ts.map +1 -0
  40. package/dist/utils/versions.js +5 -0
  41. package/generators.json +10 -0
  42. package/migrations.json +4 -0
  43. package/package.json +48 -59
  44. package/dist/package.json +0 -84
  45. package/dist/src/index.d.ts +0 -2
  46. package/dist/src/index.d.ts.map +0 -1
  47. package/dist/src/index.js +0 -7
  48. package/dist/src/plugins/plugin.d.ts.map +0 -1
  49. package/dist/src/plugins/plugin.js +0 -130
  50. package/dist/src/utils/cache.d.ts.map +0 -1
  51. package/dist/src/utils/dependency-detection.d.ts +0 -5
  52. package/dist/src/utils/dependency-detection.d.ts.map +0 -1
  53. package/dist/src/utils/dependency-detection.js +0 -58
  54. package/dist/src/utils/dotnet-cli.d.ts +0 -9
  55. package/dist/src/utils/dotnet-cli.d.ts.map +0 -1
  56. package/dist/src/utils/dotnet-cli.js +0 -39
  57. package/dist/src/utils/dotnet-project-parser.d.ts +0 -13
  58. package/dist/src/utils/dotnet-project-parser.d.ts.map +0 -1
  59. package/dist/src/utils/dotnet-project-parser.js +0 -83
  60. package/dist/src/utils/logger.d.ts +0 -3
  61. package/dist/src/utils/logger.d.ts.map +0 -1
  62. package/dist/src/utils/logger.js +0 -24
  63. package/dist/src/utils/target-builder.d.ts +0 -12
  64. package/dist/src/utils/target-builder.d.ts.map +0 -1
  65. package/dist/src/utils/target-builder.js +0 -525
  66. package/plugin.ts +0 -5
  67. package/src/index.ts +0 -6
  68. package/src/plugins/plugin.ts +0 -230
  69. package/src/utils/cache.ts +0 -19
  70. package/src/utils/dependency-detection.ts +0 -84
  71. package/src/utils/dotnet-cli.ts +0 -52
  72. package/src/utils/dotnet-project-parser.ts +0 -105
  73. package/src/utils/logger.ts +0 -24
  74. package/src/utils/target-builder.ts +0 -657
  75. package/tsconfig.json +0 -16
  76. package/tsconfig.lib.json +0 -21
  77. /package/dist/{src/utils → utils}/cache.d.ts +0 -0
  78. /package/dist/{src/utils → utils}/cache.js +0 -0
@@ -1,230 +0,0 @@
1
- import { join, basename, dirname, parse } from 'node:path';
2
- import {
3
- CreateNodes,
4
- CreateNodesContext,
5
- createNodesFromFiles,
6
- CreateNodesV2,
7
- CreateDependencies,
8
- DependencyType,
9
- logger,
10
- RawProjectGraphDependency,
11
- } from '@nx/devkit';
12
- import { calculateHashesForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
13
- import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
14
- import { hashObject } from 'nx/src/hasher/file-hasher';
15
-
16
- // Import utilities
17
- import {
18
- readTargetsCache,
19
- writeTargetsToCache,
20
- DotNetTargets,
21
- } from '../utils/cache';
22
- import { NativeDotNetClient } from '../utils/dotnet-cli';
23
- import { inferProjectName } from '../utils/dotnet-project-parser';
24
- import { buildDotNetTargets, NormalizedOptions } from '../utils/target-builder';
25
- import {
26
- createProjectRootMappings,
27
- resolveReferenceToProject,
28
- } from '../utils/dependency-detection';
29
- import { verboseLog, verboseError } from '../utils/logger';
30
-
31
- export interface DotNetPluginOptions {
32
- buildTargetName?: string;
33
- testTargetName?: string;
34
- cleanTargetName?: string;
35
- restoreTargetName?: string;
36
- publishTargetName?: string;
37
- packTargetName?: string;
38
- }
39
-
40
- const dotnetProjectGlob = '**/*.{csproj,fsproj,vbproj}';
41
-
42
- export const createNodesV2: CreateNodesV2<DotNetPluginOptions> = [
43
- dotnetProjectGlob,
44
- async (configFilePaths, options, context) => {
45
- const optionsHash = hashObject(options);
46
- const cachePath = join(
47
- workspaceDataDirectory,
48
- `dotnet-${optionsHash}.hash`
49
- );
50
- const targetsCache = readTargetsCache(cachePath);
51
- const normalizedOptions = normalizeOptions(options);
52
-
53
- // Group project files by their project root
54
- const projectRootToFiles = new Map<string, string[]>();
55
- for (const configFile of configFilePaths) {
56
- const projectRoot = dirname(configFile);
57
- if (!projectRootToFiles.has(projectRoot)) {
58
- projectRootToFiles.set(projectRoot, []);
59
- }
60
- projectRootToFiles.get(projectRoot)!.push(configFile);
61
- }
62
-
63
- const projectRoots = Array.from(projectRootToFiles.keys());
64
-
65
- // Calculate all hashes at once
66
- const hashes = await calculateHashesForCreateNodes(
67
- projectRoots,
68
- normalizedOptions,
69
- context
70
- );
71
-
72
- try {
73
- return await createNodesFromFiles(
74
- (projectRoot, options, context, idx) =>
75
- createNodesInternal(
76
- projectRoot,
77
- options,
78
- context,
79
- targetsCache,
80
- projectRootToFiles.get(projectRoot)!,
81
- hashes[idx]
82
- ),
83
- projectRoots,
84
- options,
85
- context
86
- );
87
- } finally {
88
- writeTargetsToCache(cachePath, targetsCache);
89
- }
90
- },
91
- ];
92
-
93
- async function createNodesInternal(
94
- projectRoot: string,
95
- options: DotNetPluginOptions,
96
- context: CreateNodesContext,
97
- targetsCache: Record<string, DotNetTargets>,
98
- projectFiles: string[],
99
- hash: string
100
- ) {
101
- // For .NET projects, we don't require package.json or project.json
102
- // The .csproj/.fsproj/.vbproj file itself indicates a valid project
103
-
104
- const normalizedOptions = normalizeOptions(options);
105
-
106
- targetsCache[hash] ??= await buildDotNetTargets(
107
- projectRoot,
108
- projectFiles,
109
- normalizedOptions,
110
- context
111
- );
112
- const { targets, metadata } = targetsCache[hash];
113
-
114
- // For project naming, use the directory name when multiple projects,
115
- // or the single project file name when only one
116
- const projectName =
117
- projectFiles.length === 1
118
- ? inferProjectName(projectFiles[0])
119
- : basename(projectRoot)
120
- .replace(/[^a-z0-9\-]/gi, '-')
121
- .toLowerCase();
122
-
123
- return {
124
- projects: {
125
- [projectRoot]: {
126
- name: projectName,
127
- root: projectRoot,
128
- targets,
129
- metadata,
130
- },
131
- },
132
- };
133
- }
134
-
135
- function normalizeOptions(options: DotNetPluginOptions): NormalizedOptions {
136
- return {
137
- buildTargetName: options?.buildTargetName ?? 'build',
138
- testTargetName: options?.testTargetName ?? 'test',
139
- cleanTargetName: options?.cleanTargetName ?? 'clean',
140
- restoreTargetName: options?.restoreTargetName ?? 'restore',
141
- publishTargetName: options?.publishTargetName ?? 'publish',
142
- packTargetName: options?.packTargetName ?? 'pack',
143
- };
144
- }
145
-
146
- export const createDependencies: CreateDependencies<
147
- DotNetPluginOptions
148
- > = async (_, ctx) => {
149
- const dependencies: RawProjectGraphDependency[] = [];
150
-
151
- const rootMap = createProjectRootMappings(ctx.projects);
152
-
153
- verboseLog('[dotnet] createDependencies: Starting dependency detection');
154
- verboseLog(`[dotnet] Workspace root: ${ctx.workspaceRoot}`);
155
- verboseLog(
156
- `[dotnet] Number of projects: ${Object.keys(ctx.projects).length}`
157
- );
158
- verboseLog(`[dotnet] Projects: ${Object.keys(ctx.projects).join(', ')}`);
159
- verboseLog(`[dotnet] Root map entries: ${Object.keys(rootMap).length}`);
160
- verboseLog(`[dotnet] Root map: ${JSON.stringify(rootMap, null, 2)}`);
161
-
162
- // Use dotnet CLI for dependency detection
163
- const dotnetClient = new NativeDotNetClient(ctx.workspaceRoot);
164
-
165
- for (const [source] of Object.entries(ctx.projects)) {
166
- const files = ctx.filesToProcess.projectFileMap[source] || [];
167
- verboseLog(
168
- `[dotnet] Processing project: ${source} (${files.length} files)`
169
- );
170
-
171
- for (const file of files) {
172
- const { ext } = parse(file.file);
173
- verboseLog(`[dotnet] File: ${file.file} (ext: ${ext})`);
174
- if (['.csproj', '.fsproj', '.vbproj'].includes(ext)) {
175
- try {
176
- const fullPath = join(ctx.workspaceRoot, file.file);
177
- verboseLog(`[dotnet] Getting references for: ${fullPath}`);
178
- const references = await dotnetClient.getProjectReferencesAsync(
179
- fullPath
180
- );
181
- verboseLog(
182
- `[dotnet] Found ${references.length} references: ${JSON.stringify(
183
- references
184
- )}`
185
- );
186
-
187
- for (const reference of references) {
188
- verboseLog(`[dotnet] Resolving reference: ${reference}`);
189
- const target = resolveReferenceToProject(
190
- reference,
191
- file.file,
192
- rootMap,
193
- ctx.workspaceRoot
194
- );
195
- verboseLog(`[dotnet] Resolved to target: ${target}`);
196
- if (target) {
197
- dependencies.push({
198
- source,
199
- target,
200
- type: DependencyType.static,
201
- sourceFile: file.file,
202
- });
203
- verboseLog(
204
- `[dotnet] ✓ Added dependency: ${source} -> ${target}`
205
- );
206
- } else {
207
- verboseLog(
208
- `[dotnet] ✗ Could not resolve reference to project`
209
- );
210
- }
211
- }
212
- } catch (error) {
213
- verboseError(
214
- `[dotnet] Failed to get dependencies for ${file.file}: ${error.message}`
215
- );
216
- verboseError(`[dotnet] Error stack: ${error.stack}`);
217
- }
218
- }
219
- }
220
- }
221
-
222
- verboseLog(
223
- `[dotnet] createDependencies: Completed with ${dependencies.length} dependencies`
224
- );
225
- verboseLog(
226
- `[dotnet] Final dependencies: ${JSON.stringify(dependencies, null, 2)}`
227
- );
228
-
229
- return dependencies;
230
- };
@@ -1,19 +0,0 @@
1
- import { ProjectConfiguration, readJsonFile, writeJsonFile } from '@nx/devkit';
2
- import { existsSync } from 'fs';
3
-
4
- export type DotNetTargets = Pick<ProjectConfiguration, 'targets' | 'metadata'>;
5
-
6
- export function readTargetsCache(
7
- cachePath: string
8
- ): Record<string, DotNetTargets> {
9
- return process.env.NX_CACHE_PROJECT_GRAPH !== 'false' && existsSync(cachePath)
10
- ? readJsonFile(cachePath)
11
- : {};
12
- }
13
-
14
- export function writeTargetsToCache(
15
- cachePath: string,
16
- results: Record<string, DotNetTargets>
17
- ) {
18
- writeJsonFile(cachePath, results);
19
- }
@@ -1,84 +0,0 @@
1
- import { dirname, relative, resolve, normalize } from 'node:path';
2
- import { ProjectConfiguration, normalizePath } from '@nx/devkit';
3
- import { verboseLog } from './logger';
4
-
5
- export function createProjectRootMappings(
6
- projects: Record<string, ProjectConfiguration>
7
- ): Record<string, string> {
8
- const rootMap: Record<string, string> = {};
9
- verboseLog('[dotnet-deps] Creating project root mappings');
10
- for (const [, project] of Object.entries(projects)) {
11
- if (project.root && project.name) {
12
- rootMap[project.root] = project.name;
13
- verboseLog(`[dotnet-deps] Mapping: ${project.root} -> ${project.name}`);
14
- }
15
- }
16
- verboseLog(`[dotnet-deps] Created ${Object.keys(rootMap).length} mappings`);
17
- return rootMap;
18
- }
19
-
20
- export function findProjectForPath(
21
- filePath: string,
22
- rootMap: Record<string, string>
23
- ): string | undefined {
24
- let currentPath = normalizePath(filePath);
25
- verboseLog(`[dotnet-deps] Finding project for path: ${filePath}`);
26
- verboseLog(`[dotnet-deps] Normalized path: ${currentPath}`);
27
-
28
- const checkedPaths: string[] = [];
29
- for (
30
- ;
31
- currentPath !== dirname(currentPath);
32
- currentPath = dirname(currentPath)
33
- ) {
34
- checkedPaths.push(currentPath);
35
- const project = rootMap[currentPath];
36
- if (project) {
37
- verboseLog(`[dotnet-deps] Found project: ${project} at ${currentPath}`);
38
- return project;
39
- }
40
- }
41
-
42
- const result = rootMap[currentPath];
43
- checkedPaths.push(currentPath);
44
- verboseLog(`[dotnet-deps] Checked paths: ${checkedPaths.join(' -> ')}`);
45
- verboseLog(`[dotnet-deps] Result: ${result || 'NOT FOUND'}`);
46
- return result;
47
- }
48
-
49
- export function resolveReferenceToProject(
50
- reference: string,
51
- sourceFile: string,
52
- rootMap: Record<string, string>,
53
- workspaceRoot: string
54
- ): string | undefined {
55
- verboseLog(`[dotnet-deps] Resolving reference to project:`);
56
- verboseLog(`[dotnet-deps] Reference: ${reference}`);
57
- verboseLog(`[dotnet-deps] Source file: ${sourceFile}`);
58
- verboseLog(`[dotnet-deps] Workspace root: ${workspaceRoot}`);
59
-
60
- const resolved = resolve(workspaceRoot, dirname(sourceFile), reference);
61
- verboseLog(`[dotnet-deps] Resolved absolute path: ${resolved}`);
62
-
63
- const relativePath = relative(workspaceRoot, resolved);
64
- verboseLog(`[dotnet-deps] Relative path (raw): ${relativePath}`);
65
-
66
- // First normalize path separators (backslashes to forward slashes)
67
- // Then resolve .. segments
68
- const pathWithForwardSlashes = normalizePath(relativePath);
69
- verboseLog(
70
- `[dotnet-deps] Path with forward slashes: ${pathWithForwardSlashes}`
71
- );
72
-
73
- const normalizedRelativePath = normalize(pathWithForwardSlashes);
74
- verboseLog(
75
- `[dotnet-deps] Relative path (normalized): ${normalizedRelativePath}`
76
- );
77
-
78
- const result = findProjectForPath(normalizedRelativePath, rootMap);
79
- verboseLog(
80
- `[dotnet-deps] Final resolved project: ${result || 'NOT FOUND'}`
81
- );
82
-
83
- return result;
84
- }
@@ -1,52 +0,0 @@
1
- import { execSync } from 'node:child_process';
2
- import { verboseLog, verboseError } from './logger';
3
-
4
- export interface DotNetClient {
5
- getProjectReferencesAsync(projectFile: string): Promise<string[]>;
6
- }
7
-
8
- export class NativeDotNetClient implements DotNetClient {
9
- constructor(private workspaceRoot: string) {}
10
-
11
- async getProjectReferencesAsync(projectFile: string): Promise<string[]> {
12
- try {
13
- verboseLog(`[dotnet-cli] Getting references for: ${projectFile}`);
14
- verboseLog(`[dotnet-cli] Working directory: ${this.workspaceRoot}`);
15
- const command = `dotnet list "${projectFile}" reference`;
16
- verboseLog(`[dotnet-cli] Command: ${command}`);
17
-
18
- const output = execSync(command, {
19
- cwd: this.workspaceRoot,
20
- encoding: 'utf8',
21
- windowsHide: true,
22
- stdio: ['pipe', 'pipe', 'ignore'], // Suppress stderr
23
- });
24
-
25
- verboseLog(`[dotnet-cli] Raw output (${output.length} chars):`);
26
- verboseLog(
27
- `[dotnet-cli] ${output.split('\n').join('\n[dotnet-cli] ')}`
28
- );
29
-
30
- const references = output
31
- .split('\n')
32
- .slice(2) // Skip header lines
33
- .map((line) => line.trim())
34
- .filter((line) => line.length > 0);
35
-
36
- verboseLog(
37
- `[dotnet-cli] Parsed references (${
38
- references.length
39
- }): ${JSON.stringify(references)}`
40
- );
41
- return references;
42
- } catch (error) {
43
- verboseError(
44
- `[dotnet-cli] Failed to get project references for ${projectFile}: ${error.message}`
45
- );
46
- verboseError(
47
- `[dotnet-cli] Error details: ${JSON.stringify(error, null, 2)}`
48
- );
49
- return [];
50
- }
51
- }
52
- }
@@ -1,105 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { dirname, basename, parse } from 'node:path';
3
-
4
- export interface ProjectInfo {
5
- targetFramework?: string;
6
- outputType?: string;
7
- packageReferences: string[];
8
- projectReferences: string[];
9
- isTestProject: boolean;
10
- isExecutable: boolean;
11
- projectType: 'csharp' | 'fsharp' | 'vb';
12
- }
13
-
14
- export function parseProjectFile(projectFilePath: string): ProjectInfo {
15
- const content = readFileSync(projectFilePath, 'utf-8');
16
- const packageReferences: string[] = [];
17
- const projectReferences: string[] = [];
18
-
19
- // Simple regex parsing for MSBuild project files
20
- const packageReferenceRegex = /<PackageReference\s+Include="([^"]+)"/g;
21
- const projectReferenceRegex = /<ProjectReference\s+Include="([^"]+)"/g;
22
- const targetFrameworkRegex = /<TargetFramework>([^<]+)<\/TargetFramework>/;
23
- const outputTypeRegex = /<OutputType>([^<]+)<\/OutputType>/;
24
-
25
- let match: RegExpExecArray | null;
26
- while ((match = packageReferenceRegex.exec(content)) !== null) {
27
- packageReferences.push(match[1]);
28
- }
29
-
30
- while ((match = projectReferenceRegex.exec(content)) !== null) {
31
- projectReferences.push(match[1]);
32
- }
33
-
34
- const targetFrameworkMatch = content.match(targetFrameworkRegex);
35
- const outputTypeMatch = content.match(outputTypeRegex);
36
-
37
- const targetFramework = targetFrameworkMatch?.[1];
38
- const outputType = outputTypeMatch?.[1];
39
-
40
- // Detect test projects by common test package references
41
- const testPackages = [
42
- 'Microsoft.NET.Test.Sdk',
43
- 'xunit',
44
- 'xunit.runner.visualstudio',
45
- 'MSTest.TestAdapter',
46
- 'MSTest.TestFramework',
47
- 'NUnit',
48
- 'NUnit3TestAdapter',
49
- ];
50
- const isTestProject = packageReferences.some((pkg) =>
51
- testPackages.some((testPkg) => pkg.includes(testPkg))
52
- );
53
-
54
- const isExecutable =
55
- outputType?.toLowerCase() === 'exe' ||
56
- content.includes('<OutputType>Exe</OutputType>');
57
-
58
- // Determine project type from file extension
59
- const { ext } = parse(projectFilePath);
60
- let projectType: 'csharp' | 'fsharp' | 'vb';
61
- switch (ext) {
62
- case '.fsproj':
63
- projectType = 'fsharp';
64
- break;
65
- case '.vbproj':
66
- projectType = 'vb';
67
- break;
68
- default:
69
- projectType = 'csharp';
70
- }
71
-
72
- return {
73
- targetFramework,
74
- outputType,
75
- packageReferences,
76
- projectReferences,
77
- isTestProject,
78
- isExecutable,
79
- projectType,
80
- };
81
- }
82
-
83
- export function inferProjectName(configFilePath: string): string {
84
- const { name } = parse(configFilePath);
85
-
86
- // Convert PascalCase to kebab-case for regular project names
87
- return name
88
- .replace(/([a-z])([A-Z])/g, '$1-$2')
89
- .toLowerCase()
90
- .replace(/[^a-z0-9\-]/g, '-')
91
- .replace(/-+/g, '-')
92
- .replace(/^-|-$/g, '');
93
- }
94
-
95
- export function extractProjectNameFromFile(projectFileName: string): string {
96
- const { name } = parse(projectFileName);
97
-
98
- // Convert PascalCase to kebab-case for target names
99
- return name
100
- .replace(/([a-z])([A-Z])/g, '$1-$2')
101
- .toLowerCase()
102
- .replace(/[^a-z0-9\-]/g, '-')
103
- .replace(/-+/g, '-')
104
- .replace(/^-|-$/g, '');
105
- }
@@ -1,24 +0,0 @@
1
- import { logger } from '@nx/devkit';
2
-
3
- let verboseLoggingEnabled: boolean | undefined;
4
-
5
- function isVerboseLoggingEnabled(): boolean {
6
- if (verboseLoggingEnabled === undefined) {
7
- verboseLoggingEnabled =
8
- process.env.NX_DOTNET_VERBOSE_LOGGING === 'true' ||
9
- process.env.NX_VERBOSE_LOGGING === 'true';
10
- }
11
- return verboseLoggingEnabled;
12
- }
13
-
14
- export function verboseLog(message: string): void {
15
- if (isVerboseLoggingEnabled()) {
16
- logger.log(message);
17
- }
18
- }
19
-
20
- export function verboseError(message: string): void {
21
- if (isVerboseLoggingEnabled()) {
22
- logger.error(message);
23
- }
24
- }