Skip to content

Commit 17606de

Browse files
committed
feat(pnpm): a pnpm addon for simple-release
1 parent 5cc6e7a commit 17606de

File tree

11 files changed

+490
-0
lines changed

11 files changed

+490
-0
lines changed

packages/pnpm/.eslintrc.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"extends": [
3+
"@trigen/eslint-config/typescript",
4+
"@trigen/eslint-config/typescript-requiring-type-checking",
5+
"@trigen/eslint-config/jest"
6+
],
7+
"parserOptions": {
8+
"tsconfigRootDir": "./packages/pnpm",
9+
"project": ["./tsconfig.json"]
10+
}
11+
}

packages/pnpm/README.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# @simple-release/pnpm
2+
3+
[![ESM-only package][package]][package-url]
4+
[![NPM version][npm]][npm-url]
5+
[![Node version][node]][node-url]
6+
[![Dependencies status][deps]][deps-url]
7+
[![Install size][size]][size-url]
8+
[![Build status][build]][build-url]
9+
[![Coverage status][coverage]][coverage-url]
10+
11+
[package]: https://img.shields.io/badge/package-ESM--only-ffe536.svg
12+
[package-url]: https://nodejs.org/api/esm.html
13+
14+
[npm]: https://img.shields.io/npm/v/@simple-release/pnpm.svg
15+
[npm-url]: https://www.npmjs.com/package/@simple-release/pnpm
16+
17+
[node]: https://img.shields.io/node/v/@simple-release/pnpm.svg
18+
[node-url]: https://nodejs.org
19+
20+
[deps]: https://img.shields.io/librariesio/release/npm/@simple-release/pnpm
21+
[deps-url]: https://libraries.io/npm/@simple-release%2Fcore/tree
22+
23+
[size]: https://packagephobia.com/badge?p=@simple-release/pnpm
24+
[size-url]: https://packagephobia.com/result?p=@simple-release/pnpm
25+
26+
[build]: https://img.shields.io/github/actions/workflow/status/TrigenSoftware/simple-release-tools/tests.yml?branch=main
27+
[build-url]: https://github.com/TrigenSoftware/simple-release-tools/actions
28+
29+
[coverage]: https://coveralls.io/repos/github/TrigenSoftware/simple-release-tools/badge.svg?branch=main
30+
[coverage-url]: https://coveralls.io/github/TrigenSoftware/simple-release-tools?branch=main
31+
32+
A pnpm addon for simple-release.
33+
34+
## Install
35+
36+
```bash
37+
# pnpm
38+
pnpm add @simple-release/pnpm
39+
# yarn
40+
yarn add @simple-release/pnpm
41+
# npm
42+
npm i @simple-release/pnpm
43+
```
44+
45+
## Usage
46+
47+
```js
48+
import { Releaser } from '@simple-release/core'
49+
import { PnpmProject } from '@simple-release/pnpm'
50+
51+
const project = new PnpmProject()
52+
53+
await new Releaser(project)
54+
.bump()
55+
.commit()
56+
.tag()
57+
.push()
58+
.publish()
59+
.run()
60+
```
61+
62+
Workspaces example:
63+
64+
```js
65+
import { Releaser } from '@simple-release/core'
66+
import { PnpmWorkspacesProject } from '@simple-release/pnpm'
67+
68+
const project = new PnpmWorkspacesProject({
69+
mode: 'independent'
70+
})
71+
72+
await new Releaser(project)
73+
.bump()
74+
.commit()
75+
.tag()
76+
.push()
77+
.publish()
78+
.run()
79+
```
80+
81+
`PnpmWorkspacesProject` will read workspaces from the `pnpm-workspace.yaml` in the root of the project.
82+
83+
## Options
84+
85+
### PnpmProject
86+
87+
#### `path`
88+
89+
Path to the `package.json` manifest file. Defaults to `'package.json'`.
90+
91+
#### `changelogFile`
92+
93+
Path to the changelog file. Defaults to `'CHANGELOG.md'`.
94+
95+
#### `compose`
96+
97+
Function to compose the main manifest with secondaries. It can be needed if you have some secondary manifests where version also should be updated. Optional.
98+
99+
```js
100+
import { ComposedProjectManifest } from '@simple-release/core'
101+
import { PnpmProject } from '@simple-release/pnpm'
102+
103+
new PnpmProject({
104+
compose: main => new ComposedProjectManifest(main, [
105+
new SomeManifest(/* ... */)
106+
])
107+
})
108+
```
109+
110+
### PnpmWorkspacesProject
111+
112+
#### `mode`
113+
114+
Mode to determine how to bump versions in the monorepo. Required.
115+
116+
- `independent` - each package can have its own version.
117+
- `fixed` - all packages have the same version.
118+
119+
#### `root`
120+
121+
Path to the monorepo root. Defaults to the current working directory.
122+
123+
#### `changelogFile`
124+
125+
Path to the changelog file. Defaults to `'CHANGELOG.md'`.
126+
127+
#### `compose`
128+
129+
Function to compose the main manifest with secondaries. It can be needed if you have some secondary manifests where version also should be updated. Will be called for each manifest in monorepo. Optional.
130+
131+
```js
132+
import { ComposedProjectManifest } from '@simple-release/core'
133+
import { PnpmProject } from '@simple-release/pnpm'
134+
135+
new PnpmProject({
136+
compose: (main, isRoot) => (
137+
isRoot
138+
? main
139+
: new ComposedProjectManifest(main, [
140+
new SomeManifest(/* ... */)
141+
])
142+
)
143+
})
144+
```
145+
146+
#### `scope`
147+
148+
Function to format scope name from the package name. By default, scope part of the package name will dropped (`@scope/pkg-name` -> `pkg-name`).
149+
150+
#### `tagPrefix`
151+
152+
Function to format tag prefix from scope name. By default, tag prefix will be the scope name with `@` sign (`pkg-name` -> `pkg-name@`) for independent mode and empty string for fixed mode.
153+
154+
### publish
155+
156+
Publish options for `PnpmProject` and `PnpmWorkspacesProject`.
157+
158+
#### `access`
159+
160+
Access level for the package. Optional.
161+
162+
#### `tag`
163+
164+
String or function to format tag name. Function accepts version string and prerelease versions. Optional.
165+
166+
#### `otp`
167+
168+
One-time password for publishing. Optional.
169+
170+
#### `gitChecks`
171+
172+
Whether to run git checks before publishing. Defaults to `true`.
173+
174+
#### `env`
175+
176+
Environment variables to set before publishing. Defaults to `process.env`.

packages/pnpm/package.json

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"name": "@simple-release/pnpm",
3+
"type": "module",
4+
"version": "1.0.0",
5+
"description": "A pnpm addon for simple-release.",
6+
"author": {
7+
"name": "Dan Onoshko",
8+
"email": "[email protected]",
9+
"url": "https://github.com/dangreen"
10+
},
11+
"license": "MIT",
12+
"homepage": "https://github.com/TrigenSoftware/simple-release-tools/tree/master/packages/pnpm#readme",
13+
"funding": "https://ko-fi.com/dangreen",
14+
"repository": {
15+
"type": "git",
16+
"url": "https://github.com/TrigenSoftware/simple-release-tools.git",
17+
"directory": "packages/pnpm"
18+
},
19+
"bugs": {
20+
"url": "https://github.com/TrigenSoftware/simple-release-tools/issues"
21+
},
22+
"keywords": [
23+
"simple-release",
24+
"pnpm",
25+
"release",
26+
"publish",
27+
"changelog"
28+
],
29+
"engines": {
30+
"node": ">=18"
31+
},
32+
"exports": "./src/index.ts",
33+
"publishConfig": {
34+
"exports": {
35+
"types": "./dist/index.d.ts",
36+
"import": "./dist/index.js"
37+
},
38+
"directory": "package",
39+
"linkDirectory": false
40+
},
41+
"files": [
42+
"dist"
43+
],
44+
"scripts": {
45+
"clear:package": "del ./package",
46+
"clear:dist": "del ./dist",
47+
"clear": "del ./package ./dist ./coverage",
48+
"prepublishOnly": "run build clear:package clean-publish",
49+
"postpublish": "pnpm clear:package",
50+
"build": "tsc -p tsconfig.build.json",
51+
"lint": "eslint --parser-options tsconfigRootDir:. '**/*.{js,ts}'",
52+
"test:unit": "vitest run --coverage",
53+
"test:types": "tsc --noEmit",
54+
"test": "run -p lint test:unit test:types"
55+
},
56+
"dependencies": {
57+
"@simple-libs/child-process-utils": "^1.0.0",
58+
"@simple-release/core": "workspace:^",
59+
"fast-glob": "^3.3.3",
60+
"read-yaml-file": "^2.1.0"
61+
},
62+
"devDependencies": {
63+
"test": "workspace:^"
64+
}
65+
}

packages/pnpm/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './project.js'
2+
export * from './workspacesProject.js'

packages/pnpm/src/project.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {
2+
type PackageJsonProjectOptions,
3+
type GenericProjectBumpOptions,
4+
PackageJsonProject
5+
} from '@simple-release/core'
6+
import {
7+
type PublishOptions,
8+
publish
9+
} from './publish.js'
10+
11+
export type PnpmProjectOptions = PackageJsonProjectOptions
12+
13+
export type PnpmProjectBumpOptions = GenericProjectBumpOptions
14+
15+
export type PnpmProjectPublishOptions = Omit<PublishOptions, 'workspaces'>
16+
17+
/**
18+
* A pnpm based project that uses package.json for configuration.
19+
*/
20+
export class PnpmProject extends PackageJsonProject {
21+
override async publish(options: PnpmProjectPublishOptions = {}): Promise<void> {
22+
await publish(this, options)
23+
}
24+
}

packages/pnpm/src/publish.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { spawn } from 'child_process'
2+
import { throwProcessError } from '@simple-libs/child-process-utils'
3+
import {
4+
type GenericProjectPublishOptions,
5+
type PackageJsonProject
6+
} from '@simple-release/core'
7+
8+
export interface PublishOptions extends GenericProjectPublishOptions {
9+
access?: string
10+
tag?: string | ((version: string, prerelease: readonly (string | number)[] | null) => string)
11+
otp?: string
12+
gitChecks?: boolean
13+
env?: Record<string, string | undefined>
14+
workspaces?: boolean
15+
}
16+
17+
export async function publish(project: PackageJsonProject, options: PublishOptions = {}): Promise<void> {
18+
const { manifest } = project
19+
const {
20+
access,
21+
tag,
22+
otp,
23+
gitChecks = true,
24+
env = process.env,
25+
workspaces,
26+
dryRun,
27+
logger
28+
} = options
29+
const silent = logger?.parent.options.silent
30+
const publishTag = typeof tag === 'function'
31+
? tag(
32+
await manifest.getVersion(),
33+
await manifest.getPrereleaseVersion()
34+
)
35+
: tag
36+
37+
await throwProcessError(spawn(
38+
'pnpm',
39+
[
40+
'publish',
41+
...access ? ['--access', access] : [],
42+
...publishTag ? ['--tag', publishTag] : [],
43+
...workspaces ? ['--recursive'] : [],
44+
...dryRun ? ['--dry-run'] : [],
45+
...otp ? ['--otp', otp] : [],
46+
...gitChecks ? [] : ['--no-git-checks']
47+
],
48+
{
49+
cwd: manifest.projectPath,
50+
env,
51+
stdio: silent ? 'ignore' : 'inherit'
52+
}
53+
))
54+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import fs from 'fs/promises'
2+
import { join } from 'path'
3+
import {
4+
describe,
5+
it,
6+
expect
7+
} from 'vitest'
8+
import {
9+
toArray,
10+
packageJsonIndependentMonorepoProject,
11+
forkProject
12+
} from 'test'
13+
import { PnpmWorkspacesProject } from './workspacesProject.js'
14+
15+
describe('pnpm', () => {
16+
describe('PnpmWorkspacesProject', () => {
17+
it('should parse workspaces from pnpm-workspace.yaml', async () => {
18+
const path = await forkProject('pnpm-workspaces', packageJsonIndependentMonorepoProject())
19+
20+
await fs.writeFile(join(path, 'pnpm-workspace.yaml'), `packages:
21+
- packages/subproject-1
22+
- packages/subproject-2
23+
- packages/subproject-3
24+
`)
25+
26+
const project = new PnpmWorkspacesProject({
27+
mode: 'independent',
28+
root: path
29+
})
30+
const workspaces = await toArray(project.getProjects())
31+
32+
expect(workspaces).toMatchObject([
33+
expect.objectContaining({
34+
manifest: expect.objectContaining({
35+
manifestPath: expect.stringContaining('packages/subproject-1/package.json')
36+
})
37+
}),
38+
expect.objectContaining({
39+
manifest: expect.objectContaining({
40+
manifestPath: expect.stringContaining('packages/subproject-2/package.json')
41+
})
42+
}),
43+
expect.objectContaining({
44+
manifest: expect.objectContaining({
45+
manifestPath: expect.stringContaining('packages/subproject-3/package.json')
46+
})
47+
})
48+
])
49+
})
50+
})
51+
})

0 commit comments

Comments
 (0)