Skip to content

Commit c3d11c2

Browse files
committed
fix: move path parameter to load
BREAKING CHANGE: the `path` parameter is now tied to `load` and not the class constructor.
1 parent 1d474ef commit c3d11c2

4 files changed

Lines changed: 104 additions & 157 deletions

File tree

README.md

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,24 @@ await pkgJson.save()
5252

5353
## API:
5454

55-
### `constructor(path)`
55+
### `constructor()`
5656

57-
Creates a new instance of `PackageJson`.
58-
59-
- `path`: `String` that points to the folder from where to read the
60-
`package.json` from
57+
Creates a new empty instance of `PackageJson`.
6158

6259
---
6360

64-
### `async PackageJson.load()`
61+
### `async PackageJson.load(path)`
6562

66-
Loads the `package.json` at location determined in the `path` option of
67-
the constructor.
63+
Loads the `package.json` at the given path.
6864

6965
### Example:
7066

7167
Loads contents of the `package.json` file located at `./`:
7268

7369
```js
7470
const PackageJson = require('@npmcli/package-json')
75-
const pkgJson = new PackageJson('./')
76-
await pkgJson.load()
71+
const pkgJson = new PackageJson()
72+
await pkgJson.load('./')
7773
```
7874

7975
Throws an error in case the `package.json` file is missing or has invalid
@@ -102,29 +98,27 @@ const pkgJson = await PackageJson.load('./')
10298

10399
### `async PackageJson.normalize()`
104100

105-
Like `load` but intended for reading package.json files in a
106-
node_modules tree. Some light normalization is done to ensure that it
107-
is ready for use in `@npmcli/arborist`
101+
Intended for normalizing package.json files in a node_modules tree.
102+
Some light normalization is done to ensure that it is ready for use in
103+
`@npmcli/arborist`
108104

109105
---
110106

111107
### **static** `async PackageJson.normalize(path)`
112108

113-
Convenience static method like `load` but for calling `normalize`
114-
115-
---
109+
Convenience static that calls `load` before calling `normalize`
116110

117111
---
118112

119113
### `async PackageJson.prepare()`
120114

121-
Like `load` but intended for reading package.json files before publish.
115+
Like `normalize` but intended for preparing package.json files for publish.
122116

123117
---
124118

125119
### **static** `async PackageJson.prepare(path)`
126120

127-
Convenience static method like `load` but for calling `prepare`
121+
Convenience static that calls `load` before calling `prepare`
128122

129123
---
130124

@@ -137,7 +131,7 @@ Updates the contents of the `package.json` with the `content` provided.
137131

138132
Special properties like `dependencies`, `devDependencies`,
139133
`optionalDependencies`, `peerDependencies` will have special logic to handle
140-
the update of these options, such as deduplications.
134+
the update of these options, such as sorting and deduplication.
141135

142136
### Example:
143137

@@ -200,22 +194,8 @@ pkgJson.content
200194

201195
### `async PackageJson.save()`
202196

203-
Saves the current `content` to the same location used when initializing
204-
this instance.
205-
206-
<br />
207-
208-
## Related
209-
210-
When you make a living out of reading and writing `package.json` files, you end
211-
up with quite the amount of packages dedicated to it, the **npm cli** also
212-
uses:
213-
214-
- [read-package-json-fast](https://github.com/npm/read-package-json-fast) reads
215-
and normalizes `package.json` files the way the **npm cli** expects it.
216-
- [read-package-json](https://github.com/npm/read-package-json) reads and
217-
normalizes more info from your `package.json` file. Used by `npm@6` and in
218-
`npm@7` for publishing.
197+
Saves the current `content` to the same location used when calling
198+
`load()`.
219199

220200
## LICENSE
221201

lib/index.js

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

55-
// default behavior, just loads and parses
55+
// default behavior, loads a package.json at given path and JSON parses
5656
static async load (path) {
57-
return await new PackageJson(path).load()
57+
const p = new PackageJson()
58+
return p.load(path)
5859
}
5960

6061
// read-package-json compatible behavior
6162
static async prepare (path, opts) {
62-
return await new PackageJson(path).prepare(opts)
63+
const p = new PackageJson()
64+
await p.load(path, true)
65+
return p.prepare(opts)
6366
}
6467

6568
// read-package-json-fast compatible behavior
6669
static async normalize (path, opts) {
67-
return await new PackageJson(path).normalize(opts)
70+
const p = new PackageJson()
71+
await p.load(path)
72+
return p.normalize(opts)
6873
}
6974

70-
#filename
7175
#path
72-
#manifest = {}
76+
#manifest
7377
#readFileContent = ''
74-
#fromIndex = false
78+
#canSave = true
7579

76-
constructor (path) {
80+
// Load content from given path
81+
async load (path, parseIndex) {
7782
this.#path = path
78-
this.#filename = resolve(path, 'package.json')
79-
}
80-
81-
async load (parseIndex) {
8283
let parseErr
8384
try {
84-
this.#readFileContent =
85-
await readFile(this.#filename, 'utf8')
85+
this.#readFileContent = await readFile(this.filename, 'utf8')
8686
} catch (err) {
8787
err.message = `Could not read package.json: ${err}`
8888
if (!parseIndex) {
@@ -92,31 +92,58 @@ class PackageJson {
9292
}
9393

9494
if (parseErr) {
95-
const indexFile = resolve(this.#path, 'index.js')
95+
const indexFile = resolve(this.path, 'index.js')
9696
let indexFileContent
9797
try {
9898
indexFileContent = await readFile(indexFile, 'utf8')
9999
} catch (err) {
100100
throw parseErr
101101
}
102102
try {
103-
this.#manifest = fromComment(indexFileContent)
103+
this.fromComment(indexFileContent)
104104
} catch (err) {
105105
throw parseErr
106106
}
107-
this.#fromIndex = true
107+
// This wasn't a package.json so prevent saving
108+
this.#canSave = false
108109
return this
109110
}
110111

112+
return this.fromJSON(this.#readFileContent)
113+
}
114+
115+
// Load data from a JSON string/buffer
116+
fromJSON (data) {
111117
try {
112-
this.#manifest = parseJSON(this.#readFileContent)
118+
this.#manifest = parseJSON(data)
113119
} catch (err) {
114120
err.message = `Invalid package.json: ${err}`
115121
throw err
116122
}
117123
return this
118124
}
119125

126+
// Load data from a comment
127+
// /**package { "name": "foo", "version": "1.2.3", ... } **/
128+
fromComment (data) {
129+
data = data.split(/^\/\*\*package(?:\s|$)/m)
130+
131+
if (data.length < 2) {
132+
throw new Error('File has no package in comments')
133+
}
134+
data = data[1]
135+
data = data.split(/\*\*\/$/m)
136+
137+
if (data.length < 2) {
138+
throw new Error('File has no package in comments')
139+
}
140+
data = data[0]
141+
data = data.replace(/^\s*\*/mg, '')
142+
143+
this.#manifest = parseJSON(data)
144+
return this
145+
}
146+
120147
get content () {
121148
return this.#manifest
122149
}
@@ -125,58 +152,59 @@ class PackageJson {
125152
return this.#path
126153
}
127154

128-
update (content) {
129-
// validates both current manifest and content param
130-
const invalidContent =
131-
typeof this.#manifest !== 'object'
132-
|| typeof content !== 'object'
133-
if (invalidContent) {
134-
throw Object.assign(
135-
new Error(`Can't update invalid package.json data`),
136-
{ code: 'EPACKAGEJSONUPDATE' }
137-
)
155+
get filename () {
156+
if (this.path) {
157+
return resolve(this.path, 'package.json')
138158
}
159+
return undefined
160+
}
139161

162+
update (content) {
163+
if (!this.content) {
164+
throw new Error('Can not update without existing data')
165+
}
140166
for (const step of knownSteps) {
141-
this.#manifest = step({ content, originalContent: this.#manifest })
167+
this.#manifest = step({ content, originalContent: this.content })
142168
}
143169

144170
// unknown properties will just be overwitten
145171
for (const [key, value] of Object.entries(content)) {
146172
if (!knownKeys.has(key)) {
147-
this.#manifest[key] = value
173+
this.content[key] = value
148174
}
149175
}
150176

151177
return this
152178
}
153179

154180
async save () {
155-
if (this.#fromIndex) {
181+
if (!this.#canSave) {
156182
throw new Error('No package.json to save to')
157183
}
158184
const {
159185
[Symbol.for('indent')]: indent,
160186
[Symbol.for('newline')]: newline,
161-
} = this.#manifest
187+
} = this.content
162188

163-
const format = indent === undefined ? ' ' : indent
164-
const eol = newline === undefined ? '\n' : newline
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
165194
const fileContent = `${
166-
JSON.stringify(this.#manifest, null, format)
195+
JSON.stringify(this.content, null, format)
167196
}\n`
168197
.replace(/\n/g, eol)
169198

170199
if (fileContent.trim() !== this.#readFileContent.trim()) {
171-
return await writeFile(this.#filename, fileContent)
200+
return await writeFile(this.filename, fileContent)
172201
}
173202
}
174203

175204
async normalize (opts = {}) {
176205
if (!opts.steps) {
177206
opts.steps = this.constructor.normalizeSteps
178207
}
179-
await this.load()
180208
await normalize(this, opts)
181209
return this
182210
}
@@ -185,29 +213,9 @@ class PackageJson {
185213
if (!opts.steps) {
186214
opts.steps = this.constructor.prepareSteps
187215
}
188-
await this.load(true)
189216
await normalize(this, opts)
190217
return this
191218
}
192219
}
193220

194-
// /**package { "name": "foo", "version": "1.2.3", ... } **/
195-
function fromComment (data) {
196-
data = data.split(/^\/\*\*package(?:\s|$)/m)
197-
198-
if (data.length < 2) {
199-
throw new Error('File has no package in comments')
200-
}
201-
data = data[1]
202-
data = data.split(/\*\*\/$/m)
203-
204-
if (data.length < 2) {
205-
throw new Error('File has no package in comments')
206-
}
207-
data = data[0]
208-
data = data.replace(/^\s*\*/mg, '')
209-
210-
return parseJSON(data)
211-
}
212-
213221
module.exports = PackageJson

lib/normalize.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ const log = require('proc-log')
77
const git = require('@npmcli/git')
88

99
const normalize = async (pkg, { strict, steps, root }) => {
10+
if (!pkg.content) {
11+
throw new Error('Can not normalize without existing data')
12+
}
1013
const data = pkg.content
1114
const scripts = data.scripts || {}
1215
const pkgId = `${data.name ?? ''}@${data.version ?? ''}`

0 commit comments

Comments
 (0)