Skip to content

Commit

Permalink
fix(core): enforce fixed versions only for apps with createPackageJson (
Browse files Browse the repository at this point in the history
  • Loading branch information
meeroslav committed Apr 20, 2023
1 parent 8400484 commit 4a4fc19
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,8 @@ describe('updatePackageJson', () => {
expect(distPackageJson).toMatchInlineSnapshot(`
{
"dependencies": {
"external1": "1.0.0",
"external2": "4.5.6",
"external1": "~1.0.0",
"external2": "^4.0.0",
},
"main": "./main.js",
"name": "@org/lib1",
Expand Down
216 changes: 215 additions & 1 deletion packages/nx/src/plugins/js/package-json/create-package-json.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import * as fs from 'fs';

import * as configModule from '../../../config/configuration';
import { DependencyType } from '../../../config/project-graph';
import { DependencyType, ProjectGraph } from '../../../config/project-graph';
import * as hashModule from '../../../hasher/hasher';
import { createPackageJson } from './create-package-json';
import * as fileutilsModule from '../../../utils/fileutils';

jest.mock('../../../utils/workspace-root', () => ({
workspaceRoot: '/root',
}));

describe('createPackageJson', () => {
it('should add additional dependencies', () => {
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
Expand Down Expand Up @@ -499,4 +503,214 @@ describe('createPackageJson', () => {
);
expect(filterUsingGlobPatternsSpy).toHaveBeenCalledTimes(4);
});

describe('parsing "package.json" versions', () => {
const appDependencies = [
{ source: 'app1', target: 'npm:@nx/devkit', type: 'static' },
{ source: 'app1', target: 'npm:typescript', type: 'static' },
];

const libDependencies = [
{ source: 'lib1', target: 'npm:@nx/devkit', type: 'static' },
{ source: 'lib1', target: 'npm:tslib', type: 'static' },
{ source: 'lib1', target: 'npm:typescript', type: 'static' },
];

const graph: ProjectGraph = {
nodes: {
app1: {
type: 'app',
name: 'app1',
data: {
files: [
{
file: '/root/apps/app1/src/main.ts',
hash: '',
dependencies: appDependencies,
},
],
targets: {},
root: '/root/apps/app1',
},
},
lib1: {
type: 'lib',
name: 'lib1',
data: {
files: [
{
file: '/root/libs/lib1/index.ts',
hash: '',
dependencies: libDependencies,
},
],
targets: {},
root: '/root/libs/lib1',
},
},
},
externalNodes: {
'npm:@nx/devkit': {
type: 'npm',
name: 'npm:@nx/devkit',
data: { version: '16.0.0', hash: '', packageName: '@nx/devkit' },
},
'npm:nx': {
type: 'npm',
name: 'npm:nx',
data: { version: '16.0.0', hash: '', packageName: 'nx' },
},
'npm:tslib': {
type: 'npm',
name: 'npm:tslib',
data: { version: '2.4.4', hash: '', packageName: 'tslib' },
},
'npm:typescript': {
type: 'npm',
name: 'npm:typescript',
data: { version: '4.9.5', hash: '', packageName: 'typescript' },
},
},
dependencies: {
app1: appDependencies,
lib1: libDependencies,
},
};

const rootPackageJson = () => ({
dependencies: {
'@nx/devkit': '~16.0.0',
nx: '> 14',
typescript: '^4.8.2',
tslib: '~2.4.0',
},
});

const projectPackageJson = () => ({
name: 'other-name',
version: '1.2.3',
dependencies: {
typescript: '^4.8.4',
random: '1.0.0',
},
});

const spies = [];

afterEach(() => {
while (spies.length > 0) {
spies.pop().mockRestore();
}
});

it('should use fixed versions when creating package json for apps', () => {
spies.push(
jest
.spyOn(fileutilsModule, 'readJsonFile')
.mockImplementation((path) => {
if (path === '/root/package.json') {
return rootPackageJson();
}
})
);

expect(createPackageJson('app1', graph)).toEqual({
dependencies: {
'@nx/devkit': '16.0.0',
nx: '16.0.0',
typescript: '4.9.5',
},
name: 'app1',
version: '0.0.1',
});
});

it('should override fixed versions with local ranges when creating package json for apps', () => {
spies.push(
jest.spyOn(fs, 'existsSync').mockImplementation((path) => {
if (path === '/root/apps/app1/package.json') {
return true;
}
})
);
spies.push(
jest
.spyOn(fileutilsModule, 'readJsonFile')
.mockImplementation((path) => {
if (path === '/root/package.json') {
return rootPackageJson();
}
if (path === '/root/apps/app1/package.json') {
return projectPackageJson();
}
})
);

expect(createPackageJson('app1', graph)).toEqual({
dependencies: {
'@nx/devkit': '16.0.0',
nx: '16.0.0',
random: '1.0.0',
typescript: '^4.8.4',
},
name: 'other-name',
version: '1.2.3',
});
});

it('should use range versions when creating package json for libs', () => {
spies.push(
jest
.spyOn(fileutilsModule, 'readJsonFile')
.mockImplementation((path) => {
if (path === '/root/package.json') {
return rootPackageJson();
}
})
);

expect(createPackageJson('lib1', graph)).toEqual({
dependencies: {
'@nx/devkit': '~16.0.0',
tslib: '~2.4.0',
typescript: '^4.8.2',
},
name: 'lib1',
version: '0.0.1',
});
});

it('should override range versions with local ranges when creating package json for libs', () => {
spies.push(
jest.spyOn(fs, 'existsSync').mockImplementation((path) => {
if (path === '/root/libs/lib1/package.json') {
return true;
}
})
);
spies.push(
jest
.spyOn(fileutilsModule, 'readJsonFile')
.mockImplementation((path) => {
if (path === '/root/package.json') {
return rootPackageJson();
}
if (path === '/root/libs/lib1/package.json') {
return projectPackageJson();
}
})
);

expect(createPackageJson('lib1', graph)).toEqual({
dependencies: {
'@nx/devkit': '~16.0.0',
random: '1.0.0',
tslib: '~2.4.0',
typescript: '^4.8.4',
},
name: 'other-name',
version: '1.2.3',
});
});
});
});
89 changes: 61 additions & 28 deletions packages/nx/src/plugins/js/package-json/create-package-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export function createPackageJson(
} = {}
): PackageJson {
const projectNode = graph.nodes[projectName];
const isLibrary = projectNode.type === 'lib';

const { selfInputs, dependencyInputs } = options.target
? getTargetInputs(readNxJson(), projectNode, options.target)
Expand Down Expand Up @@ -91,6 +92,18 @@ export function createPackageJson(
} catch (e) {}
}

const getVersion = (
packageName,
version,
section: 'devDependencies' | 'dependencies'
) => {
return (
packageJson[section][packageName] ||
(isLibrary && rootPackageJson[section]?.[packageName]) ||
version
);
};

const rootPackageJson = readJsonFile(
`${options.root || workspaceRoot}/package.json`
);
Expand All @@ -103,44 +116,64 @@ export function createPackageJson(
// don't store dev dependencies for production
if (!options.isProduction) {
packageJson.devDependencies ??= {};
packageJson.devDependencies[packageName] = version;
packageJson.devDependencies[packageName] = getVersion(
packageName,
version,
'devDependencies'
);
}
} else {
if (!packageJson.peerDependencies?.[packageName]) {
packageJson.dependencies ??= {};
packageJson.dependencies[packageName] = version;
packageJson.dependencies[packageName] = getVersion(
packageName,
version,
'dependencies'
);
}
}
});
Object.entries(npmDeps.peerDependencies).forEach(([packageName, version]) => {
if (!packageJson.peerDependencies?.[packageName]) {
if (rootPackageJson.dependencies?.[packageName]) {
packageJson.dependencies ??= {};
packageJson.dependencies[packageName] = version;
return;
}
if (!isLibrary) {
Object.entries(npmDeps.peerDependencies).forEach(
([packageName, version]) => {
if (!packageJson.peerDependencies?.[packageName]) {
if (rootPackageJson.dependencies?.[packageName]) {
packageJson.dependencies ??= {};
packageJson.dependencies[packageName] = getVersion(
packageName,
version,
'dependencies'
);
return;
}

const isOptionalPeer =
npmDeps.peerDependenciesMeta[packageName]?.optional;
if (!isOptionalPeer) {
if (
!options.isProduction ||
rootPackageJson.dependencies?.[packageName]
) {
packageJson.peerDependencies ??= {};
packageJson.peerDependencies[packageName] = version;
const isOptionalPeer =
npmDeps.peerDependenciesMeta[packageName]?.optional;
if (!isOptionalPeer) {
if (
!options.isProduction ||
rootPackageJson.dependencies?.[packageName]
) {
packageJson.peerDependencies ??= {};
packageJson.peerDependencies[packageName] = getVersion(
packageName,
version,
'dependencies'
);
}
} else if (!options.isProduction) {
// add peer optional dependencies if not in production
packageJson.peerDependencies ??= {};
packageJson.peerDependencies[packageName] = version;
packageJson.peerDependenciesMeta ??= {};
packageJson.peerDependenciesMeta[packageName] = {
optional: true,
};
}
}
} else if (!options.isProduction) {
// add peer optional dependencies if not in production
packageJson.peerDependencies ??= {};
packageJson.peerDependencies[packageName] = version;
packageJson.peerDependenciesMeta ??= {};
packageJson.peerDependenciesMeta[packageName] = {
optional: true,
};
}
}
});
);
}

packageJson.devDependencies &&= sortObjectByKeys(packageJson.devDependencies);
packageJson.dependencies &&= sortObjectByKeys(packageJson.dependencies);
Expand Down

0 comments on commit 4a4fc19

Please sign in to comment.