diff --git a/docs/generated/devkit/ProjectGraphProjectNode.md b/docs/generated/devkit/ProjectGraphProjectNode.md index 12a785f2aa..a641649d80 100644 --- a/docs/generated/devkit/ProjectGraphProjectNode.md +++ b/docs/generated/devkit/ProjectGraphProjectNode.md @@ -8,7 +8,7 @@ A node describing a project in a workspace - [data](../../devkit/documents/ProjectGraphProjectNode#data): ProjectConfiguration & Object - [name](../../devkit/documents/ProjectGraphProjectNode#name): string -- [type](../../devkit/documents/ProjectGraphProjectNode#type): "app" | "e2e" | "lib" +- [type](../../devkit/documents/ProjectGraphProjectNode#type): "lib" | "app" | "e2e" ## Properties @@ -28,4 +28,4 @@ Additional metadata about a project ### type -• **type**: `"app"` \| `"e2e"` \| `"lib"` +• **type**: `"lib"` \| `"app"` \| `"e2e"` diff --git a/e2e/nx/src/graph-ts-solution.test.ts b/e2e/nx/src/graph-ts-solution.test.ts index cb0e95c67d..60e96139dd 100644 --- a/e2e/nx/src/graph-ts-solution.test.ts +++ b/e2e/nx/src/graph-ts-solution.test.ts @@ -154,6 +154,30 @@ describe('Graph - TS solution setup', () => { }, }, }); + // wildcard exports with trailing values + createPackage('pkg15', { + sourceFilePaths: ['src/features/some-file.ts'], + packageJsonEntryFields: { + exports: { + './features/*.js': { + types: './dist/src/features/*.d.ts', + default: './dist/src/features/*.js', + }, + }, + }, + }); + // wildcard exports with no leading or trailing values + createPackage('pkg16', { + sourceFilePaths: ['src/features/subpath/extra-nested/index.ts'], + packageJsonEntryFields: { + exports: { + './*': { + types: './dist/src/features/*/index.d.ts', + default: './dist/src/features/*/index.js', + }, + }, + }, + }); // project outside of the package manager workspaces createPackage('lib1', { root: 'libs/lib1' }); @@ -173,6 +197,8 @@ describe('Graph - TS solution setup', () => { json.dependencies['@proj/pkg10'] = 'workspace:*'; json.dependencies['@proj/pkg11'] = 'workspace:*'; json.dependencies['@proj/pkg13'] = 'workspace:*'; + json.dependencies['@proj/pkg15'] = 'workspace:*'; + json.dependencies['@proj/pkg16'] = 'workspace:*'; return json; }); } @@ -207,6 +233,8 @@ describe('Graph - TS solution setup', () => { { path: '../pkg12' }, { path: '../pkg13' }, { path: '../pkg14' }, + { path: '../pkg15' }, + { path: '../pkg16' }, ]; return json; }); @@ -226,13 +254,15 @@ describe('Graph - TS solution setup', () => { import { util1 } from '@proj/pkg11/utils/util1'; import { pkg12 } from '@proj/pkg12/feature1'; import { pkg13 } from '@proj/pkg14'; + import { some_file } from '@proj/pkg15/features/some-file.js'; + import { pkg16 } from '@proj/pkg16/subpath/extra-nested'; // this is an invalid import that doesn't match any TS path alias and // it's not included in the package manager workspaces, it should not // be picked up as a dependency import { lib1 } from '@proj/lib1'; // use the correct imports, leave out the invalid ones so it's easier to remove them later - export const pkgParent = pkg2 + pkg4 + pkg5 + pkg6 + pkg7 + pkg8 + pkg9 + pkg10 + util1 + pkg13; + export const pkgParent = pkg2 + pkg4 + pkg5 + pkg6 + pkg7 + pkg8 + pkg9 + pkg10 + util1 + pkg13 + some_file + pkg16; ` ); @@ -253,6 +283,8 @@ describe('Graph - TS solution setup', () => { '@proj/pkg10', '@proj/pkg11', '@proj/pkg13', + '@proj/pkg15', + '@proj/pkg16', ]); // assert build fails due to the invalid imports @@ -331,7 +363,7 @@ describe('Graph - TS solution setup', () => { const sourceFilePaths = options?.sourceFilePaths ?? ['src/index.ts']; for (const sourceFilePath of sourceFilePaths) { - const fileName = basename(sourceFilePath, '.ts'); + const fileName = basename(sourceFilePath, '.ts').replace(/-/g, '_'); createFile( `${root}/${sourceFilePath}`, `export const ${ diff --git a/packages/nx/src/plugins/js/project-graph/build-dependencies/target-project-locator.spec.ts b/packages/nx/src/plugins/js/project-graph/build-dependencies/target-project-locator.spec.ts index c40bae9830..5f208cf340 100644 --- a/packages/nx/src/plugins/js/project-graph/build-dependencies/target-project-locator.spec.ts +++ b/packages/nx/src/plugins/js/project-graph/build-dependencies/target-project-locator.spec.ts @@ -1129,6 +1129,10 @@ describe('TargetProjectLocator', () => { ${{ '.': 'dist/index.js' }} | ${'@org/pkg1'} ${{ './subpath': './dist/subpath.js' }} | ${'@org/pkg1/subpath'} ${{ './*': './dist/*.js' }} | ${'@org/pkg1/subpath'} + ${{ './*': './dist/*.js' }} | ${'@org/pkg1/subpath/extra-path'} + ${{ './*': './dist/foo/*/index.js' }} | ${'@org/pkg1/foo/subpath'} + ${{ './*': './dist/foo/*/index.js' }} | ${'@org/pkg1/foo/subpath/extra-path'} + ${{ './features/*.js': './dist/features/*.js' }} | ${'@org/pkg1/features/some-file.js'} ${{ import: './dist/index.js', default: './dist/index.js' }} | ${'@org/pkg1'} `( 'should find "$importPath" as "pkg1" project when exports="$exports"', @@ -1168,6 +1172,10 @@ describe('TargetProjectLocator', () => { ${{ '.': 'dist/index.js' }} | ${'@org/pkg1'} ${{ './subpath': './dist/subpath.js' }} | ${'@org/pkg1/subpath'} ${{ './*': './dist/*.js' }} | ${'@org/pkg1/subpath'} + ${{ './*': './dist/*.js' }} | ${'@org/pkg1/subpath/extra-path'} + ${{ './*': './dist/foo/*/index.js' }} | ${'@org/pkg1/foo/subpath'} + ${{ './*': './dist/foo/*/index.js' }} | ${'@org/pkg1/foo/subpath/extra-path'} + ${{ './features/*.js': './dist/features/*.js' }} | ${'@org/pkg1/features/some-file.js'} ${{ import: './dist/index.js', default: './dist/index.js' }} | ${'@org/pkg1'} `( 'should not find "$importPath" as "pkg1" project when exports="$exports" and isInPackageManagerWorkspaces is false', @@ -1206,8 +1214,8 @@ describe('TargetProjectLocator', () => { ${undefined} | ${'@org/pkg1'} ${{}} | ${'@org/pkg1'} ${{ '.': 'dist/index.js' }} | ${'@org/pkg1/subpath'} + ${{ './subpath/*': 'dist/subpath/*.js' }} | ${'@org/pkg1/foo'} ${{ './subpath': './dist/subpath.js' }} | ${'@org/pkg1/subpath/extra-path'} - ${{ './*': './dist/*.js' }} | ${'@org/pkg1/subpath/extra-path'} ${{ './feature': null }} | ${'@org/pkg1/feature'} ${{ import: './dist/index.js', default: './dist/index.js' }} | ${'@org/pkg1/subpath'} `( diff --git a/packages/nx/src/plugins/js/utils/packages.ts b/packages/nx/src/plugins/js/utils/packages.ts index 349f38b8bd..12cae46dca 100644 --- a/packages/nx/src/plugins/js/utils/packages.ts +++ b/packages/nx/src/plugins/js/utils/packages.ts @@ -1,4 +1,3 @@ -import { minimatch } from 'minimatch'; import { join } from 'node:path/posix'; import type { ProjectGraphProjectNode } from '../../../config/project-graph'; import type { ProjectConfiguration } from '../../../config/workspace-json-project-json'; @@ -79,6 +78,8 @@ export function getWorkspacePackagesMetadata< }; } +// adapted from PACKAGE_IMPORTS_EXPORTS_RESOLVE at +// https://nodejs.org/docs/latest-v22.x/api/esm.html#resolution-algorithm-specification export function matchImportToWildcardEntryPointsToProjectMap< T extends ProjectGraphProjectNode | ProjectConfiguration >( @@ -89,9 +90,33 @@ export function matchImportToWildcardEntryPointsToProjectMap< return null; } - const matchingPair = Object.entries(wildcardEntryPointsToProjectMap).find( - ([key]) => minimatch(importPath, key) + const entryPoint = Object.keys(wildcardEntryPointsToProjectMap).find( + (key) => { + const segments = key.split('*'); + if (segments.length > 2) { + return false; + } + + const patternBase = segments[0]; + if (patternBase === importPath) { + return false; + } + + if (!importPath.startsWith(patternBase)) { + return false; + } + + const patternTrailer = segments[1]; + if ( + patternTrailer?.length > 0 && + (!importPath.endsWith(patternTrailer) || importPath.length < key.length) + ) { + return false; + } + + return true; + } ); - return matchingPair?.[1]; + return entryPoint ? wildcardEntryPointsToProjectMap[entryPoint] : null; }