Skip to content

Commit 5061aff

Browse files
authored
feat: add bump-packages generator (#19)
1 parent a6489fb commit 5061aff

File tree

8 files changed

+207
-0
lines changed

8 files changed

+207
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"nx": "15.4.1",
6464
"parse-version-string": "^1.0.1",
6565
"prettier": "2.7.1",
66+
"semver": "^7.5.4",
6667
"ts-jest": "28.0.8",
6768
"ts-node": "10.9.1",
6869
"tslib": "^2.0.0",

packages/plugin-tools/generators.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
"factory": "./src/generators/config",
4343
"schema": "./src/generators/config/schema.json",
4444
"description": "Configure workspace scope."
45+
},
46+
"bump-packages": {
47+
"factory": "./src/generators/bump-packages/generator",
48+
"schema": "./src/generators/bump-packages/schema.json",
49+
"description": "bump-packages generator"
4550
}
4651
}
4752
}

packages/plugin-tools/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"lint-staged": "^13.0.0",
4747
"nativescript-theme-core": "~1.0.4",
4848
"sass": "^1.35.0",
49+
"semver": "^7.5.4",
4950
"parse-version-string": "^1.0.1",
5051
"prettier": "^2.7.0",
5152
"pretty-data": "^0.40.0",
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Tree, addProjectConfiguration } from '@nrwl/devkit';
2+
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
3+
4+
import generator from './generator';
5+
6+
describe('bump-packages generator', () => {
7+
let appTree: Tree;
8+
9+
beforeEach(() => {
10+
appTree = createTreeWithEmptyWorkspace();
11+
appTree.write('apps/app/package.json', JSON.stringify({ version: '1.0.0' }));
12+
appTree.write('packages/lib/package.json', JSON.stringify({ version: '1.0.0' }));
13+
appTree.write('packages/lib2/package.json', JSON.stringify({ version: '2.0.0' }));
14+
addProjectConfiguration(appTree, 'app', {
15+
root: 'apps/app',
16+
projectType: 'application',
17+
tags: ['tag1', 'tag2'],
18+
});
19+
addProjectConfiguration(appTree, 'lib', {
20+
root: 'packages/lib',
21+
projectType: 'library',
22+
tags: ['tag2'],
23+
});
24+
addProjectConfiguration(appTree, 'lib2', {
25+
root: 'packages/lib2',
26+
projectType: 'library',
27+
tags: ['tag1'],
28+
});
29+
});
30+
31+
it('should bump all by patch', async () => {
32+
await generator(appTree, {
33+
targetVersion: 'patch',
34+
});
35+
expect(JSON.parse(appTree.read('apps/app/package.json').toString()).version).toBe('1.0.1');
36+
expect(JSON.parse(appTree.read('packages/lib/package.json').toString()).version).toBe('1.0.1');
37+
expect(JSON.parse(appTree.read('packages/lib2/package.json').toString()).version).toBe('2.0.1');
38+
});
39+
40+
it('should filter by type', async () => {
41+
await generator(appTree, {
42+
targetVersion: 'patch',
43+
projectType: 'library',
44+
});
45+
expect(JSON.parse(appTree.read('apps/app/package.json').toString()).version).toBe('1.0.0');
46+
expect(JSON.parse(appTree.read('packages/lib/package.json').toString()).version).toBe('1.0.1');
47+
expect(JSON.parse(appTree.read('packages/lib2/package.json').toString()).version).toBe('2.0.1');
48+
});
49+
it('should filter by tag', async () => {
50+
await generator(appTree, {
51+
targetVersion: 'patch',
52+
tags: 'tag2',
53+
});
54+
expect(JSON.parse(appTree.read('apps/app/package.json').toString()).version).toBe('1.0.1');
55+
expect(JSON.parse(appTree.read('packages/lib/package.json').toString()).version).toBe('1.0.1');
56+
expect(JSON.parse(appTree.read('packages/lib2/package.json').toString()).version).toBe('2.0.0');
57+
});
58+
59+
it('should fail to set non-semver', async () => {
60+
const gen = await generator(appTree, {
61+
targetVersion: 'not-semver',
62+
}).catch(() => 'failed');
63+
expect(gen).toBe('failed');
64+
});
65+
it('should fail to set version lower than the existing version', async () => {
66+
const gen = await generator(appTree, {
67+
targetVersion: '1.0.1',
68+
}).catch(() => 'failed');
69+
expect(gen).toBe('failed');
70+
});
71+
});
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { ProjectConfiguration, Tree, formatFiles, getProjects, logger } from '@nrwl/devkit';
2+
import * as semver from 'semver';
3+
import { BumpPackagesGeneratorSchema } from './schema';
4+
5+
interface NormalizedSchema extends BumpPackagesGeneratorSchema {
6+
projects: ProjectConfiguration[];
7+
versionBump: semver.ReleaseType | false;
8+
fixedVersion: string;
9+
}
10+
11+
function isVersionBump(version: string): version is semver.ReleaseType {
12+
return ['major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease'].includes(version);
13+
}
14+
15+
function normalizeOptions(tree: Tree, options: BumpPackagesGeneratorSchema): NormalizedSchema {
16+
const filters = {
17+
projectType: options.projectType ?? '',
18+
tags: options.tags?.split(',').map((s) => s.trim()),
19+
};
20+
console.log(filters);
21+
const projects = options.projectName
22+
? [getProjects(tree).get(options.projectName)]
23+
: Array.from(getProjects(tree).values()).filter((v) => {
24+
if (filters.projectType) {
25+
if (v.projectType !== filters.projectType) {
26+
return false;
27+
}
28+
}
29+
if (filters.tags) {
30+
if (!v.tags.some((t) => filters.tags.includes(t))) {
31+
return false;
32+
}
33+
}
34+
return true;
35+
});
36+
37+
const versionBump = isVersionBump(options.targetVersion) ? options.targetVersion : false;
38+
if (!versionBump) {
39+
if (!semver.parse(options.targetVersion)) {
40+
throw new Error(`Invalid version ${options.targetVersion}`);
41+
}
42+
}
43+
44+
return {
45+
...options,
46+
projects,
47+
versionBump,
48+
fixedVersion: versionBump ? '' : options.targetVersion,
49+
};
50+
}
51+
52+
export default async function (tree: Tree, options: BumpPackagesGeneratorSchema) {
53+
const normalizedOptions = normalizeOptions(tree, options);
54+
normalizedOptions.projects.forEach((project) => {
55+
const packageJson = tree.read(`${project.root}/package.json`);
56+
if (!packageJson) {
57+
throw new Error(`Could not find package.json for project ${project.name}`);
58+
}
59+
const parsedPackageJson = JSON.parse(packageJson.toString());
60+
const oldVersion = parsedPackageJson.version;
61+
const newVersion = normalizedOptions.versionBump ? semver.inc(parsedPackageJson.version, normalizedOptions.versionBump) : normalizedOptions.fixedVersion;
62+
if (!newVersion) {
63+
throw new Error(`Could not bump version for project ${project.name}`);
64+
}
65+
if (semver.gte(oldVersion, newVersion)) {
66+
throw new Error(`New version (${newVersion}) is not greater than old version (${oldVersion}) for project ${project.name}, Skipping project`);
67+
}
68+
parsedPackageJson.version = newVersion;
69+
tree.write(`${project.root}/package.json`, JSON.stringify(parsedPackageJson, null, 2));
70+
});
71+
await formatFiles(tree);
72+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface BumpPackagesGeneratorSchema {
2+
targetVersion: string;
3+
projectName?: string;
4+
tags?: string;
5+
projectType?: string;
6+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"cli": "nx",
4+
"$id": "BumpPackages",
5+
"title": "",
6+
"type": "object",
7+
"properties": {
8+
"targetVersion": {
9+
"x-prompt": "What's the new version (patch, minor, major) or explicit version number?.",
10+
"type": "string",
11+
"description": "Desired version to bump (patch, minor, major), or explicit version number (must comply with semver)",
12+
"default": "patch",
13+
"$default": {
14+
"$source": "argv",
15+
"index": 0
16+
},
17+
"anyOf": [
18+
{
19+
"enum": ["major", "premajor", "minor", "preminor", "patch", "prepatch", "prerelease"]
20+
},
21+
{
22+
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
23+
}
24+
],
25+
"x-priority": "important"
26+
},
27+
"projectName": {
28+
"description": "Project to bump. If unspecified all projects are bumped",
29+
"type": "string",
30+
"x-dropdown": "projects"
31+
},
32+
"tags": {
33+
"type": "string",
34+
"description": "Filter projects by tags (comma separated)",
35+
"alias": "t"
36+
},
37+
"projectType": {
38+
"type": "string",
39+
"enum": ["application", "library"],
40+
"default": "library"
41+
}
42+
},
43+
"required": ["targetVersion"]
44+
}

yarn.lock

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5902,6 +5902,13 @@ semver@^6.0.0, semver@^6.3.0:
59025902
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
59035903
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
59045904

5905+
semver@^7.5.4:
5906+
version "7.5.4"
5907+
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
5908+
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
5909+
dependencies:
5910+
lru-cache "^6.0.0"
5911+
59055912
59065913
version "0.18.0"
59075914
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"

0 commit comments

Comments
 (0)