Skip to content

Commit 5cc6e7a

Browse files
committed
feat(npm): a npm addon for simple-release
1 parent d695be5 commit 5cc6e7a

File tree

11 files changed

+462
-0
lines changed

11 files changed

+462
-0
lines changed

packages/npm/.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/npm",
9+
"project": ["./tsconfig.json"]
10+
}
11+
}

packages/npm/README.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# @simple-release/npm
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/npm.svg
15+
[npm-url]: https://www.npmjs.com/package/@simple-release/npm
16+
17+
[node]: https://img.shields.io/node/v/@simple-release/npm.svg
18+
[node-url]: https://nodejs.org
19+
20+
[deps]: https://img.shields.io/librariesio/release/npm/@simple-release/npm
21+
[deps-url]: https://libraries.io/npm/@simple-release%2Fcore/tree
22+
23+
[size]: https://packagephobia.com/badge?p=@simple-release/npm
24+
[size-url]: https://packagephobia.com/result?p=@simple-release/npm
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 npm addon for simple-release.
33+
34+
## Install
35+
36+
```bash
37+
# pnpm
38+
pnpm add @simple-release/npm
39+
# yarn
40+
yarn add @simple-release/npm
41+
# npm
42+
npm i @simple-release/npm
43+
```
44+
45+
## Usage
46+
47+
```js
48+
import { Releaser } from '@simple-release/core'
49+
import { NpmProject } from '@simple-release/npm'
50+
51+
const project = new NpmProject()
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 { NpmWorkspacesProject } from '@simple-release/npm'
67+
68+
const project = new NpmWorkspacesProject({
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+
`NpmWorkspacesProject` will read workspaces from the `package.json` in the root of the project.
82+
83+
## Options
84+
85+
### NpmProject
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 { NpmProject } from '@simple-release/npm'
102+
103+
new NpmProject({
104+
compose: main => new ComposedProjectManifest(main, [
105+
new SomeManifest(/* ... */)
106+
])
107+
})
108+
```
109+
110+
### NpmWorkspacesProject
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 { NpmProject } from '@simple-release/npm'
134+
135+
new NpmProject({
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 `NpmProject` and `NpmWorkspacesProject`.
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+
#### `env`
171+
172+
Environment variables to set before publishing. Defaults to `process.env`.

packages/npm/package.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"name": "@simple-release/npm",
3+
"type": "module",
4+
"version": "1.0.0",
5+
"description": "A npm 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/npm#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/npm"
18+
},
19+
"bugs": {
20+
"url": "https://github.com/TrigenSoftware/simple-release-tools/issues"
21+
},
22+
"keywords": [
23+
"simple-release",
24+
"npm",
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+
},
60+
"devDependencies": {
61+
"test": "workspace:^"
62+
}
63+
}

packages/npm/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/npm/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 NpmProjectOptions = PackageJsonProjectOptions
12+
13+
export type NpmProjectBumpOptions = GenericProjectBumpOptions
14+
15+
export type NpmProjectPublishOptions = Omit<PublishOptions, 'workspaces'>
16+
17+
/**
18+
* A npm based project that uses package.json for configuration.
19+
*/
20+
export class NpmProject extends PackageJsonProject {
21+
override async publish(options: NpmProjectPublishOptions = {}): Promise<void> {
22+
await publish(this, options)
23+
}
24+
}

packages/npm/src/publish.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
env?: Record<string, string | undefined>
13+
workspaces?: boolean
14+
}
15+
16+
export async function publish(project: PackageJsonProject, options: PublishOptions = {}): Promise<void> {
17+
const { manifest } = project
18+
const {
19+
access,
20+
tag,
21+
otp,
22+
env = process.env,
23+
workspaces,
24+
dryRun,
25+
logger
26+
} = options
27+
const silent = logger?.parent.options.silent
28+
const publishTag = typeof tag === 'function'
29+
? tag(
30+
await manifest.getVersion(),
31+
await manifest.getPrereleaseVersion()
32+
)
33+
: tag
34+
35+
await throwProcessError(spawn(
36+
'npm',
37+
[
38+
'publish',
39+
...access ? ['--access', access] : [],
40+
...publishTag ? ['--tag', publishTag] : [],
41+
...workspaces ? ['--workspaces'] : [],
42+
...dryRun ? ['--dry-run'] : [],
43+
...otp ? ['--otp', otp] : []
44+
],
45+
{
46+
cwd: manifest.projectPath,
47+
env,
48+
stdio: silent ? 'ignore' : 'inherit'
49+
}
50+
))
51+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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 { NpmWorkspacesProject } from './workspacesProject.js'
14+
15+
describe('npm', () => {
16+
describe('NpmWorkspacesProject', () => {
17+
it('should parse workspaces from package.json', async () => {
18+
const path = await forkProject('npm-workspaces', packageJsonIndependentMonorepoProject())
19+
const pkgJsonContent = await fs.readFile(join(path, 'package.json'), 'utf-8')
20+
const pkgJson = JSON.parse(pkgJsonContent)
21+
22+
pkgJson.workspaces = [
23+
'packages/subproject-1',
24+
'packages/subproject-2',
25+
'packages/subproject-3'
26+
]
27+
28+
await fs.writeFile(join(path, 'package.json'), JSON.stringify(pkgJson))
29+
30+
const project = new NpmWorkspacesProject({
31+
mode: 'independent',
32+
root: path
33+
})
34+
const workspaces = await toArray(project.getProjects())
35+
36+
expect(workspaces).toMatchObject([
37+
expect.objectContaining({
38+
manifest: expect.objectContaining({
39+
manifestPath: expect.stringContaining('packages/subproject-1/package.json')
40+
})
41+
}),
42+
expect.objectContaining({
43+
manifest: expect.objectContaining({
44+
manifestPath: expect.stringContaining('packages/subproject-2/package.json')
45+
})
46+
}),
47+
expect.objectContaining({
48+
manifest: expect.objectContaining({
49+
manifestPath: expect.stringContaining('packages/subproject-3/package.json')
50+
})
51+
})
52+
])
53+
})
54+
})
55+
})

0 commit comments

Comments
 (0)