Skip to content

Commit 4775bf3

Browse files
committed
feat: add create functionality
1 parent c3d11c2 commit 4775bf3

5 files changed

Lines changed: 261 additions & 155 deletions

File tree

README.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,36 +58,48 @@ Creates a new empty instance of `PackageJson`.
5858

5959
---
6060

61-
### `async PackageJson.load(path)`
61+
### `async PackageJson.create(path)`
6262

63-
Loads the `package.json` at the given path.
63+
Creates an empty `package.json` at the given path. If one already exists
64+
it will be overwritten.
65+
66+
---
67+
68+
### `async PackageJson.load(path, opts = {})`
69+
70+
Loads a `package.json` at the given path.
71+
72+
- `opts`: `Object` can contain:
73+
- `create`: `Boolean` if true, a new package.json will be created if
74+
one does not already exist. Will not clobber ane existing
75+
package.json that can not be parsed.
6476

6577
### Example:
6678

67-
Loads contents of the `package.json` file located at `./`:
79+
Loads contents of a `package.json` file located at `./`:
6880

6981
```js
7082
const PackageJson = require('@npmcli/package-json')
7183
const pkgJson = new PackageJson()
7284
await pkgJson.load('./')
7385
```
7486

75-
Throws an error in case the `package.json` file is missing or has invalid
87+
Throws an error in case a `package.json` file is missing or has invalid
7688
contents.
7789

7890
---
7991

8092
### **static** `async PackageJson.load(path)`
8193

8294
Convenience static method that returns a new instance and loads the contents of
83-
the `package.json` file from that location.
95+
a `package.json` file from that location.
8496

8597
- `path`: `String` that points to the folder from where to read the
8698
`package.json` from
8799

88100
### Example:
89101

90-
Loads contents of the `package.json` file located at `./`:
102+
Loads contents of a `package.json` file located at `./`:
91103

92104
```js
93105
const PackageJson = require('@npmcli/package-json')
@@ -124,7 +136,7 @@ Convenience static that calls `load` before calling `prepare`
124136

125137
### `PackageJson.update(content)`
126138

127-
Updates the contents of the `package.json` with the `content` provided.
139+
Updates the contents of a `package.json` with the `content` provided.
128140

129141
- `content`: `Object` containing the properties to be updated/replaced in the
130142
`package.json` file.

lib/index.js

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,33 @@ class PackageJson {
5252
'binRefs',
5353
])
5454

55-
// default behavior, loads a package.json at given path and JSON parses
56-
static async load (path) {
55+
// create a new empty package.json, so we can save at the given path even
56+
// though we didn't start from a parsed file
57+
static async create (path, opts = {}) {
5758
const p = new PackageJson()
58-
return p.load(path)
59+
await p.create(path)
60+
if (opts.data) {
61+
return p.update(opts.data)
62+
}
63+
return p
64+
}
65+
66+
// Loads a package.json at given path and JSON parses
67+
static async load (path, opts = {}) {
68+
const p = new PackageJson()
69+
// Avoid try/catch if we aren't going to create
70+
if (!opts.create) {
71+
return p.load(path)
72+
}
73+
74+
try {
75+
return await p.load(path)
76+
} catch (err) {
77+
if (!err.message.startsWith('Could not read package.json')) {
78+
throw err
79+
}
80+
return await p.create(path)
81+
}
5982
}
6083

6184
// read-package-json compatible behavior
@@ -159,10 +182,18 @@ class PackageJson {
159182
return undefined
160183
}
161184

185+
create (path) {
186+
this.#path = path
187+
this.#manifest = {}
188+
return this
189+
}
190+
191+
// This should be the ONLY way to set content in the manifest
162192
update (content) {
163193
if (!this.content) {
164-
throw new Error('Can not update without existing data')
194+
throw new Error('Can not update without content. Please `load` or `create`')
165195
}
196+
166197
for (const step of knownSteps) {
167198
this.#manifest = step({ content, originalContent: this.content })
168199
}
@@ -186,11 +217,8 @@ class PackageJson {
186217
[Symbol.for('newline')]: newline,
187218
} = this.content
188219

189-
// These can only be undefined if we didn't parse JSON, which we currently don't support.
190-
// const format = indent === undefined ? ' ' : indent
191-
// const eol = newline === undefined ? '\n' : newline
192-
const format = indent
193-
const eol = newline
220+
const format = indent === undefined ? ' ' : indent
221+
const eol = newline === undefined ? '\n' : newline
194222
const fileContent = `${
195223
JSON.stringify(this.content, null, format)
196224
}\n`

lib/normalize.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const git = require('@npmcli/git')
88

99
const normalize = async (pkg, { strict, steps, root }) => {
1010
if (!pkg.content) {
11-
throw new Error('Can not normalize without existing data')
11+
throw new Error('Can not normalize without content')
1212
}
1313
const data = pkg.content
1414
const scripts = data.scripts || {}

tap-snapshots/test/index.js.test.cjs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,41 @@
55
* Make sure to inspect the output below. Do not ignore changes!
66
*/
77
'use strict'
8-
exports[`test/index.js TAP custom formatting > should save back custom format to package.json 1`] = `
9-
{"name":"foo","version":"1.0.1","description":"Lorem ipsum dolor"}
8+
exports[`test/index.js TAP create with data > should save package.json with name 1`] = `
9+
{
10+
"name": "create-test"
11+
}
12+
1013
`
1114

12-
exports[`test/index.js TAP invalid package.json data > should save updated content to package.json and ignore invalid data 1`] = `
13-
this! is! not! json!
15+
exports[`test/index.js TAP create without data > should save empty package.json 1`] = `
16+
{}
17+
1418
`
1519

16-
exports[`test/index.js TAP read, update content and write > should properly save contennt to a package.json 1`] = `
17-
{
18-
"name": "foo",
19-
"version": "1.0.1",
20-
"description": "Lorem ipsum dolor"
21-
}
20+
exports[`test/index.js TAP load create:true empty dir > should save empty package.json 1`] = `
21+
{}
22+
23+
`
2224

25+
exports[`test/index.js TAP load create:true existing parseable package.json > package.json should match existing 1`] = `
26+
{"name":"test-package"}
2327
`
2428

25-
exports[`test/index.js TAP start new package.json, update and write > should properly save contentn to a package.json 1`] = `
29+
exports[`test/index.js TAP load custom formatting > should save back custom format to package.json 1`] = `
30+
{"name":"foo","version":"1.0.1","description":"Lorem ipsum dolor"}
31+
`
32+
33+
exports[`test/index.js TAP load read, update content and write > should properly save contennt to a package.json 1`] = `
2634
{
27-
"name": "foo"
35+
"name": "foo",
36+
"version": "1.0.1",
37+
"description": "Lorem ipsum dolor"
2838
}
2939
3040
`
3141

32-
exports[`test/index.js TAP update long package.json > should only update the defined property 1`] = `
42+
exports[`test/index.js TAP load update long package.json > should only update the defined property 1`] = `
3343
{
3444
"version": "7.18.1",
3545
"name": "npm",
@@ -274,7 +284,7 @@ exports[`test/index.js TAP update long package.json > should only update the def
274284
275285
`
276286

277-
exports[`test/index.js TAP update long package.json > should properly write updated pacakge.json contents 1`] = `
287+
exports[`test/index.js TAP load update long package.json > should properly write updated pacakge.json contents 1`] = `
278288
{
279289
"version": "7.18.1",
280290
"name": "npm",

0 commit comments

Comments
 (0)