Skip to content

Commit f50e71e

Browse files
davidldayLee-W
authored andcommitted
feat(provider): add npm2 provider to update package.json, package-lock.json, and npm-shrinkwrap.json
closes #804
1 parent bc11094 commit f50e71e

File tree

5 files changed

+434
-180
lines changed

5 files changed

+434
-180
lines changed

commitizen/providers.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import json
4+
import jsonpath_ng # type: ignore
45
import re
56
from abc import ABC, abstractmethod
67
from pathlib import Path
@@ -166,6 +167,87 @@ class NpmProvider(JsonProvider):
166167
filename = "package.json"
167168

168169

170+
class Npm2Provider(VersionProvider):
171+
"""
172+
npm package.json and package-lock.json version management
173+
"""
174+
175+
indent: ClassVar[int] = 2
176+
package_filename = "package.json"
177+
package_expression = "$.version"
178+
lock_filename = "package-lock.json"
179+
lock_expressions = ["$.version", "$.packages.''.version"]
180+
shrinkwrap_filename = "npm-shrinkwrap.json"
181+
shrinkwrap_expressions = ["$.version", "$.packages.''.version"]
182+
183+
@property
184+
def package_file(self) -> Path:
185+
return Path() / self.package_filename
186+
187+
@property
188+
def lock_file(self) -> Path:
189+
return Path() / self.lock_filename
190+
191+
@property
192+
def shrinkwrap_file(self) -> Path:
193+
return Path() / self.shrinkwrap_filename
194+
195+
def get_version(self) -> str:
196+
"""
197+
Get the current version from package.json
198+
"""
199+
package_document = json.loads(self.package_file.read_text())
200+
return self.get_package_version(package_document)
201+
202+
def set_version(self, version: str):
203+
package_document = self.set_package_version(
204+
json.loads(self.package_file.read_text()), version
205+
)
206+
self.package_file.write_text(
207+
json.dumps(package_document, indent=self.indent) + "\n"
208+
)
209+
if self.lock_file.exists():
210+
lock_document = self.set_lock_version(
211+
json.loads(self.lock_file.read_text()), version
212+
)
213+
self.lock_file.write_text(
214+
json.dumps(lock_document, indent=self.indent) + "\n"
215+
)
216+
if self.shrinkwrap_file.exists():
217+
shrinkwrap_document = self.set_lock_version(
218+
json.loads(self.shrinkwrap_file.read_text()), version
219+
)
220+
self.shrinkwrap_file.write_text(
221+
json.dumps(shrinkwrap_document, indent=self.indent) + "\n"
222+
)
223+
224+
def get_package_version(self, document: dict[str, Any]) -> str:
225+
expr = jsonpath_ng.parse(self.package_expression)
226+
versions = expr.find(document)
227+
if len(versions) == 0:
228+
raise ValueError("No version found")
229+
elif len(versions) > 1:
230+
raise ValueError("Multiple versions found")
231+
return versions[0].value # type: ignore
232+
233+
def set_package_version(self, document: dict[str, Any], version: str):
234+
expr = jsonpath_ng.parse(self.package_expression)
235+
document = expr.update(document, version)
236+
return document
237+
238+
def set_lock_version(self, document: dict[str, Any], version: str):
239+
for expression in self.lock_expressions:
240+
expr = jsonpath_ng.parse(expression)
241+
document = expr.update(document, version)
242+
return document
243+
244+
def set_shrinkwrap_version(self, document: dict[str, Any], version: str):
245+
for expression in self.shrinkwrap_expressions:
246+
expr = jsonpath_ng.parse(expression)
247+
document = expr.update(document, version)
248+
return document
249+
250+
169251
class ComposerProvider(JsonProvider):
170252
"""
171253
Composer version management

docs/config.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -293,15 +293,16 @@ But you can use any `commitizen.provider` entrypoint as value for `version_provi
293293

294294
Commitizen provides some version providers for some well known formats:
295295

296-
| name | description |
297-
| ------------ | --------------------------------------------------------------------- |
298-
| `commitizen` | Default version provider: Fetch and set version in commitizen config. |
299-
| `scm` | Fetch the version from git and does not need to set it back |
300-
| `pep621` | Get and set version from `pyproject.toml` `project.version` field |
301-
| `poetry` | Get and set version from `pyproject.toml` `tool.poetry.version` field |
302-
| `cargo` | Get and set version from `Cargo.toml` `project.version` field |
303-
| `npm` | Get and set version from `package.json` `project.version` field |
304-
| `composer` | Get and set version from `composer.json` `project.version` field |
296+
| name | description |
297+
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
298+
| `commitizen` | Default version provider: Fetch and set version in commitizen config. |
299+
| `scm` | Fetch the version from git and does not need to set it back |
300+
| `pep621` | Get and set version from `pyproject.toml` `project.version` field |
301+
| `poetry` | Get and set version from `pyproject.toml` `tool.poetry.version` field |
302+
| `cargo` | Get and set version from `Cargo.toml` `project.version` field |
303+
| `npm` | Get and set version from `package.json` `project.version` field |
304+
| `npm2` | Get and set version from `package.json` `$.version` field, `package-lock.json` `$.version,$.packages.''.version` fields if the file exists, and `npm-shrinkwrap.json` `$.version,$.packages.''.version` fields if the file exists |
305+
| `composer` | Get and set version from `composer.json` `project.version` field |
305306

306307
!!! note
307308
The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin.

0 commit comments

Comments
 (0)