Skip to content

Commit

Permalink
deps: @npmcli/package-json@4.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
wraithgar committed Jul 5, 2023
1 parent 690c9fc commit b252164
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 65 deletions.
172 changes: 119 additions & 53 deletions node_modules/@npmcli/package-json/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,23 @@ class PackageJson {
'bin',
])

// npm pkg fix
static fixSteps = Object.freeze([
'binRefs',
'bundleDependencies',
'bundleDependenciesFalse',
'fixNameField',
'fixVersionField',
'fixRepositoryField',
'fixBinField',
'fixDependencies',
'fixScriptsField',
'devDependencies',
'scriptpath',
])

static prepareSteps = Object.freeze([
'_id',
'_attributes',
'bundledDependencies',
'bundleDependencies',
Expand All @@ -52,37 +68,67 @@ class PackageJson {
'binRefs',
])

// default behavior, just loads and parses
static async load (path) {
return await new PackageJson(path).load()
// create a new empty package.json, so we can save at the given path even
// though we didn't start from a parsed file
static async create (path, opts = {}) {
const p = new PackageJson()
await p.create(path)
if (opts.data) {
return p.update(opts.data)
}
return p
}

// Loads a package.json at given path and JSON parses
static async load (path, opts = {}) {
const p = new PackageJson()
// Avoid try/catch if we aren't going to create
if (!opts.create) {
return p.load(path)
}

try {
return await p.load(path)
} catch (err) {
if (!err.message.startsWith('Could not read package.json')) {
throw err
}
return await p.create(path)
}
}

// npm pkg fix
static async fix (path, opts) {
const p = new PackageJson()
await p.load(path, true)
return p.fix(opts)
}

// read-package-json compatible behavior
static async prepare (path, opts) {
return await new PackageJson(path).prepare(opts)
const p = new PackageJson()
await p.load(path, true)
return p.prepare(opts)
}

// read-package-json-fast compatible behavior
static async normalize (path, opts) {
return await new PackageJson(path).normalize(opts)
const p = new PackageJson()
await p.load(path)
return p.normalize(opts)
}

#filename
#path
#manifest = {}
#manifest
#readFileContent = ''
#fromIndex = false
#canSave = true

constructor (path) {
// Load content from given path
async load (path, parseIndex) {
this.#path = path
this.#filename = resolve(path, 'package.json')
}

async load (parseIndex) {
let parseErr
try {
this.#readFileContent =
await readFile(this.#filename, 'utf8')
this.#readFileContent = await readFile(this.filename, 'utf8')
} catch (err) {
err.message = `Could not read package.json: ${err}`
if (!parseIndex) {
Expand All @@ -92,31 +138,58 @@ class PackageJson {
}

if (parseErr) {
const indexFile = resolve(this.#path, 'index.js')
const indexFile = resolve(this.path, 'index.js')
let indexFileContent
try {
indexFileContent = await readFile(indexFile, 'utf8')
} catch (err) {
throw parseErr
}
try {
this.#manifest = fromComment(indexFileContent)
this.fromComment(indexFileContent)
} catch (err) {
throw parseErr
}
this.#fromIndex = true
// This wasn't a package.json so prevent saving
this.#canSave = false
return this
}

return this.fromJSON(this.#readFileContent)
}

// Load data from a JSON string/buffer
fromJSON (data) {
try {
this.#manifest = parseJSON(this.#readFileContent)
this.#manifest = parseJSON(data)
} catch (err) {
err.message = `Invalid package.json: ${err}`
throw err
}
return this
}

// Load data from a comment
// /**package { "name": "foo", "version": "1.2.3", ... } **/
fromComment (data) {
data = data.split(/^\/\*\*package(?:\s|$)/m)

if (data.length < 2) {
throw new Error('File has no package in comments')
}
data = data[1]
data = data.split(/\*\*\/$/m)

if (data.length < 2) {
throw new Error('File has no package in comments')
}
data = data[0]
data = data.replace(/^\s*\*/mg, '')

this.#manifest = parseJSON(data)
return this
}

get content () {
return this.#manifest
}
Expand All @@ -125,58 +198,64 @@ class PackageJson {
return this.#path
}

get filename () {
if (this.path) {
return resolve(this.path, 'package.json')
}
return undefined
}

create (path) {
this.#path = path
this.#manifest = {}
return this
}

// This should be the ONLY way to set content in the manifest
update (content) {
// validates both current manifest and content param
const invalidContent =
typeof this.#manifest !== 'object'
|| typeof content !== 'object'
if (invalidContent) {
throw Object.assign(
new Error(`Can't update invalid package.json data`),
{ code: 'EPACKAGEJSONUPDATE' }
)
if (!this.content) {
throw new Error('Can not update without content. Please `load` or `create`')
}

for (const step of knownSteps) {
this.#manifest = step({ content, originalContent: this.#manifest })
this.#manifest = step({ content, originalContent: this.content })
}

// unknown properties will just be overwitten
for (const [key, value] of Object.entries(content)) {
if (!knownKeys.has(key)) {
this.#manifest[key] = value
this.content[key] = value
}
}

return this
}

async save () {
if (this.#fromIndex) {
if (!this.#canSave) {
throw new Error('No package.json to save to')
}
const {
[Symbol.for('indent')]: indent,
[Symbol.for('newline')]: newline,
} = this.#manifest
} = this.content

const format = indent === undefined ? ' ' : indent
const eol = newline === undefined ? '\n' : newline
const fileContent = `${
JSON.stringify(this.#manifest, null, format)
JSON.stringify(this.content, null, format)
}\n`
.replace(/\n/g, eol)

if (fileContent.trim() !== this.#readFileContent.trim()) {
return await writeFile(this.#filename, fileContent)
return await writeFile(this.filename, fileContent)
}
}

async normalize (opts = {}) {
if (!opts.steps) {
opts.steps = this.constructor.normalizeSteps
}
await this.load()
await normalize(this, opts)
return this
}
Expand All @@ -185,29 +264,16 @@ class PackageJson {
if (!opts.steps) {
opts.steps = this.constructor.prepareSteps
}
await this.load(true)
await normalize(this, opts)
return this
}
}

// /**package { "name": "foo", "version": "1.2.3", ... } **/
function fromComment (data) {
data = data.split(/^\/\*\*package(?:\s|$)/m)

if (data.length < 2) {
throw new Error('File has no package in comments')
}
data = data[1]
data = data.split(/\*\*\/$/m)

if (data.length < 2) {
throw new Error('File has no package in comments')
async fix (opts = {}) {
// This one is not overridable
opts.steps = this.constructor.fixSteps
await normalize(this, opts)
return this
}
data = data[0]
data = data.replace(/^\s*\*/mg, '')

return parseJSON(data)
}

module.exports = PackageJson
Loading

0 comments on commit b252164

Please sign in to comment.