Skip to content
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions e2e/release/src/preserve-matching-dependency-ranges.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ describe('nx release preserve matching dependency ranges', () => {
{project-name} 📄 Resolved the current version as 1.0.0 from manifest: {project-name}/package.json
{project-name} ❓ Applied explicit semver value "1.1.0", from the given specifier, to get new version 1.1.0
{project-name} ✍️ New version 1.1.0 written to manifest: {project-name}/package.json
{project-name} ✍️ Updated 3 dependencies in manifest: {project-name}/package.json
{project-name} ✍️ Updated 2 dependencies in manifest: {project-name}/package.json
NX Running release version for project: {project-name}
{project-name} 📄 Resolved the current version as 1.0.0 from manifest: {project-name}/package.json
{project-name} ❓ Applied version 1.1.0 directly, because the project is a member of a fixed release group containing {project-name}
Expand Down Expand Up @@ -320,7 +320,7 @@ describe('nx release preserve matching dependency ranges', () => {
{project-name} 📄 Resolved the current version as 1.0.0 from manifest: {project-name}/package.json
{project-name} ❓ Applied semver relative bump "minor", from the given specifier, to get new version 1.1.0
{project-name} ✍️ New version 1.1.0 written to manifest: {project-name}/package.json
{project-name} ✍️ Updated 3 dependencies in manifest: {project-name}/package.json
{project-name} ✍️ Updated 2 dependencies in manifest: {project-name}/package.json
NX Running release version for project: {project-name}
{project-name} 📄 Resolved the current version as 1.0.0 from manifest: {project-name}/package.json
{project-name} ❓ Applied version 1.1.0 directly, because the project is a member of a fixed release group containing {project-name}
Expand Down
4 changes: 4 additions & 0 deletions packages/devkit/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ export {
type PackageJson,
type PackageJsonDependencySection,
type PackageManagerCommands,
type ParsedDependencySpecifier,
type ProjectPackageDependencies,
type ProjectRootMappings,
RemoteCacheV2,
type RunCommandsOptions,
Expand Down Expand Up @@ -264,6 +266,7 @@ export {
normalizeTargetDependencyWithStringProjects,
nxVersion,
orange,
parseDependencySpecifier,
parseExecutor,
parseVersionFromPackageManagerField,
preventRecursionInGraphConstruction,
Expand All @@ -283,6 +286,7 @@ export {
requireWithTsconfigFallback,
resetWorkspaceContext,
resolveCommandSyntacticSugar,
resolveWorkspaceDependencyTarget,
runNxSync,
safeExecFileSync,
safeSpawn,
Expand Down
277 changes: 277 additions & 0 deletions packages/eslint-plugin/src/rules/dependency-checks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3787,6 +3787,283 @@ describe('Dependency checks (eslint)', () => {
);
});
});

describe('workspace package aliases', () => {
const aliasGraphNodes = (packageDependencies: any) => ({
liba: {
name: 'liba',
type: 'lib' as const,
data: {
root: 'libs/liba',
metadata: {
js: {
packageName: '@mycompany/liba',
packageDependencies,
},
},
targets: { build: {} },
},
},
libb: {
name: 'libb',
type: 'lib' as const,
data: {
root: 'libs/libb',
metadata: {
js: {
packageName: '@mycompany/libb',
packageVersion: '0.0.1',
isInPackageManagerWorkspaces: true,
},
},
targets: { build: {} },
},
},
});

const aliasFileSys = (packageJson: unknown) => ({
'./libs/liba/package.json': JSON.stringify(packageJson, null, 2),
'./libs/liba/src/index.ts': '',
'./libs/libb/package.json': JSON.stringify(
{ name: '@mycompany/libb', version: '0.0.1' },
null,
2
),
'./package.json': JSON.stringify(rootPackageJson, null, 2),
});

it('should not report an aliased workspace dependency as missing or obsolete', () => {
const packageJson = {
name: '@mycompany/liba',
dependencies: {
'any-alias': 'workspace:@mycompany/libb@*',
},
};
vol.fromJSON(aliasFileSys(packageJson), '/root');

const failures = runRule(
{},
`/root/libs/liba/package.json`,
JSON.stringify(packageJson, null, 2),
{
nodes: aliasGraphNodes({
dependencies: {
'any-alias': {
rawSpecifier: 'workspace:@mycompany/libb@*',
requestedPackageName: '@mycompany/libb',
},
},
}),
externalNodes,
dependencies: {
liba: [{ source: 'liba', target: 'libb', type: 'static' }],
},
},
{
liba: [createFile(`libs/liba/src/main.ts`, ['libb'])],
libb: [createFile(`libs/libb/src/index.ts`)],
}
);

expect(failures).toEqual([]);
});

it('should not report a satisfying npm alias of a workspace dependency', () => {
const packageJson = {
name: '@mycompany/liba',
dependencies: {
'any-alias': 'npm:@mycompany/libb@^0.0.1',
},
};
vol.fromJSON(aliasFileSys(packageJson), '/root');

const failures = runRule(
{},
`/root/libs/liba/package.json`,
JSON.stringify(packageJson, null, 2),
{
nodes: aliasGraphNodes({
dependencies: {
'any-alias': {
rawSpecifier: 'npm:@mycompany/libb@^0.0.1',
requestedPackageName: '@mycompany/libb',
},
},
}),
externalNodes,
dependencies: {
liba: [{ source: 'liba', target: 'libb', type: 'static' }],
},
},
{
liba: [createFile(`libs/liba/src/main.ts`, ['libb'])],
libb: [createFile(`libs/libb/src/index.ts`)],
}
);

expect(failures).toEqual([]);
});

it('should resolve a key reused across collections to the production entry', () => {
const packageJson = {
name: '@mycompany/liba',
dependencies: {
'any-alias': 'workspace:@mycompany/libb@*',
},
devDependencies: {
'any-alias': 'workspace:@mycompany/other@*',
},
};
vol.fromJSON(aliasFileSys(packageJson), '/root');

const nodes = {
...aliasGraphNodes({
dependencies: {
'any-alias': {
rawSpecifier: 'workspace:@mycompany/libb@*',
requestedPackageName: '@mycompany/libb',
},
},
devDependencies: {
'any-alias': {
rawSpecifier: 'workspace:@mycompany/other@*',
requestedPackageName: '@mycompany/other',
},
},
}),
other: {
name: 'other',
type: 'lib' as const,
data: {
root: 'libs/other',
metadata: {
js: {
packageName: '@mycompany/other',
packageVersion: '1.0.0',
isInPackageManagerWorkspaces: true,
},
},
targets: { build: {} },
},
},
};

const failures = runRule(
{},
`/root/libs/liba/package.json`,
JSON.stringify(packageJson, null, 2),
{
nodes,
externalNodes,
dependencies: {
liba: [{ source: 'liba', target: 'libb', type: 'static' }],
},
},
{
liba: [createFile(`libs/liba/src/main.ts`, ['libb'])],
libb: [createFile(`libs/libb/src/index.ts`)],
}
);

expect(failures).toEqual([]);
});

it('should not fix an npm alias mismatch to a nested workspace specifier', () => {
const packageJson = {
name: '@mycompany/liba',
dependencies: {
'any-alias': 'npm:@mycompany/libb@^9.0.0',
},
};
const fileSys = {
...aliasFileSys(packageJson),
'./package.json': JSON.stringify(
{
...rootPackageJson,
dependencies: {
...rootPackageJson.dependencies,
'@mycompany/libb': 'workspace:*',
},
},
null,
2
),
};
vol.fromJSON(fileSys, '/root');

const failures = runRule(
{},
`/root/libs/liba/package.json`,
JSON.stringify(packageJson, null, 2),
{
nodes: aliasGraphNodes({
dependencies: {
'any-alias': {
rawSpecifier: 'npm:@mycompany/libb@^9.0.0',
requestedPackageName: '@mycompany/libb',
},
},
}),
externalNodes,
dependencies: {
liba: [{ source: 'liba', target: 'libb', type: 'static' }],
},
},
{
liba: [createFile(`libs/liba/src/main.ts`, ['libb'])],
libb: [createFile(`libs/libb/src/index.ts`)],
}
);

expect(failures.length).toEqual(1);
const content = JSON.stringify(packageJson, null, 2);
const result =
content.slice(0, failures[0].fix.range[0]) +
failures[0].fix.text +
content.slice(failures[0].fix.range[1]);
expect(result).toContain('"any-alias": "npm:@mycompany/libb@0.0.1"');
});

it('should still report an aliased entry whose target is not depended on', () => {
const packageJson = {
name: '@mycompany/liba',
dependencies: {
external1: '^16.0.0',
'any-alias': 'workspace:@mycompany/libb@*',
},
};
vol.fromJSON(aliasFileSys(packageJson), '/root');

const failures = runRule(
{},
`/root/libs/liba/package.json`,
JSON.stringify(packageJson, null, 2),
{
nodes: aliasGraphNodes({
dependencies: {
'any-alias': {
rawSpecifier: 'workspace:@mycompany/libb@*',
requestedPackageName: '@mycompany/libb',
},
},
}),
externalNodes,
dependencies: {
liba: [{ source: 'liba', target: 'npm:external1', type: 'static' }],
},
},
{
liba: [createFile(`libs/liba/src/main.ts`, ['npm:external1'])],
libb: [createFile(`libs/libb/src/index.ts`)],
}
);

expect(failures.length).toEqual(1);
expect(failures[0].message).toContain(
'The "any-alias" package is not used'
);
});
});
});

function createFile(f: string, deps?: FileDataDependency[]): FileData {
Expand Down
Loading
Loading