Skip to content

Commit

Permalink
deps: @npmcli/package-json@4.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
wraithgar committed Jul 18, 2023
1 parent 3829998 commit a0763d3
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 41 deletions.
6 changes: 4 additions & 2 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,12 @@ graph LR;
npmcli-mock-registry-->npmcli-eslint-config["@npmcli/eslint-config"];
npmcli-mock-registry-->npmcli-template-oss["@npmcli/template-oss"];
npmcli-mock-registry-->pacote;
npmcli-package-json-->hosted-git-info;
npmcli-package-json-->json-parse-even-better-errors;
npmcli-package-json-->normalize-package-data;
npmcli-package-json-->npm-normalize-package-bin;
npmcli-package-json-->npmcli-git["@npmcli/git"];
npmcli-package-json-->proc-log;
npmcli-package-json-->semver;
npmcli-run-script-->npmcli-node-gyp["@npmcli/node-gyp"];
npmcli-run-script-->npmcli-promise-spawn["@npmcli/promise-spawn"];
npmcli-run-script-->read-package-json-fast;
Expand Down Expand Up @@ -700,11 +701,12 @@ graph LR;
npmcli-mock-registry-->pacote;
npmcli-mock-registry-->tap;
npmcli-package-json-->glob;
npmcli-package-json-->hosted-git-info;
npmcli-package-json-->json-parse-even-better-errors;
npmcli-package-json-->normalize-package-data;
npmcli-package-json-->npm-normalize-package-bin;
npmcli-package-json-->npmcli-git["@npmcli/git"];
npmcli-package-json-->proc-log;
npmcli-package-json-->semver;
npmcli-promise-spawn-->which;
npmcli-query-->postcss-selector-parser;
npmcli-run-script-->node-gyp;
Expand Down
2 changes: 0 additions & 2 deletions node_modules/@npmcli/package-json/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ class PackageJson {
'fixNameField',
'fixVersionField',
'fixRepositoryField',
'fixBinField',
'fixDependencies',
'fixScriptsField',
'devDependencies',
'scriptpath',
])
Expand Down
236 changes: 211 additions & 25 deletions node_modules/@npmcli/package-json/lib/normalize.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,89 @@
const semver = require('semver')
const fs = require('fs/promises')
const { glob } = require('glob')
const normalizePackageBin = require('npm-normalize-package-bin')
const legacyFixer = require('normalize-package-data/lib/fixer.js')
const legacyMakeWarning = require('normalize-package-data/lib/make_warning.js')
const path = require('path')
const log = require('proc-log')
const git = require('@npmcli/git')
const hostedGitInfo = require('hosted-git-info')

// used to be npm-normalize-package-bin
function normalizePackageBin (pkg, changes) {
if (pkg.bin) {
if (typeof pkg.bin === 'string' && pkg.name) {
changes?.push('"bin" was converted to an object')
pkg.bin = { [pkg.name]: pkg.bin }
} else if (Array.isArray(pkg.bin)) {
changes?.push('"bin" was converted to an object')
pkg.bin = pkg.bin.reduce((acc, k) => {
acc[path.basename(k)] = k
return acc
}, {})
}
if (typeof pkg.bin === 'object') {
for (const binKey in pkg.bin) {
if (typeof pkg.bin[binKey] !== 'string') {
delete pkg.bin[binKey]
changes?.push(`removed invalid "bin[${binKey}]"`)
continue
}
const base = path.join('/', path.basename(binKey.replace(/\\|:/g, '/'))).slice(1)
if (!base) {
delete pkg.bin[binKey]
changes?.push(`removed invalid "bin[${binKey}]"`)
continue
}

const binTarget = path.join('/', pkg.bin[binKey].replace(/\\/g, '/'))
.replace(/\\/g, '/').slice(1)

if (!binTarget) {
delete pkg.bin[binKey]
changes?.push(`removed invalid "bin[${binKey}]"`)
continue
}

if (base !== binKey) {
delete pkg.bin[binKey]
changes?.push(`"bin[${binKey}]" was renamed to "bin[${base}]"`)
}
if (binTarget !== pkg.bin[binKey]) {
changes?.push(`"bin[${base}]" script name was cleaned`)
}
pkg.bin[base] = binTarget
}

if (Object.keys(pkg.bin).length === 0) {
changes?.push('empty "bin" was removed')
delete pkg.bin
}

return pkg
}
}
delete pkg.bin
}

function isCorrectlyEncodedName (spec) {
return !spec.match(/[/@\s+%:]/) &&
spec === encodeURIComponent(spec)
}

function isValidScopedPackageName (spec) {
if (spec.charAt(0) !== '@') {
return false
}

const rest = spec.slice(1).split('/')
if (rest.length !== 2) {
return false
}

return rest[0] && rest[1] &&
rest[0] === encodeURIComponent(rest[0]) &&
rest[1] === encodeURIComponent(rest[1])
}

// We don't want the `changes` array in here by default because this is a hot
// path for parsing packuments during install. So the calling method passes it
Expand All @@ -18,17 +96,49 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
const scripts = data.scripts || {}
const pkgId = `${data.name ?? ''}@${data.version ?? ''}`

legacyFixer.warn = function () {
changes?.push(legacyMakeWarning.apply(null, arguments))
}

// name and version are load bearing so we have to clean them up first
if (steps.includes('fixNameField') || steps.includes('normalizeData')) {
legacyFixer.fixNameField(data, { strict, allowLegacyCase })
if (!data.name && !strict) {
changes?.push('Missing "name" field was set to an empty string')
data.name = ''
} else {
if (typeof data.name !== 'string') {
throw new Error('name field must be a string.')
}
if (!strict) {
const name = data.name.trim()
if (data.name !== name) {
changes?.push(`Whitespace was trimmed from "name"`)
data.name = name
}
}

if (data.name.startsWith('.') ||
!(isValidScopedPackageName(data.name) || isCorrectlyEncodedName(data.name)) ||
(strict && (!allowLegacyCase) && data.name !== data.name.toLowerCase()) ||
data.name.toLowerCase() === 'node_modules' ||
data.name.toLowerCase() === 'favicon.ico') {
throw new Error('Invalid name: ' + JSON.stringify(data.name))
}
}
}

if (steps.includes('fixVersionField') || steps.includes('normalizeData')) {
legacyFixer.fixVersionField(data, strict)
// allow "loose" semver 1.0 versions in non-strict mode
// enforce strict semver 2.0 compliance in strict mode
const loose = !strict
if (!data.version) {
data.version = ''
} else {
if (!semver.valid(data.version, loose)) {
throw new Error(`Invalid version: "${data.version}"`)
}
const version = semver.clean(data.version, loose)
if (version !== data.version) {
changes?.push(`"version" was cleaned and set to "${version}"`)
data.version = version
}
}
}
// remove attributes that start with "_"
if (steps.includes('_attributes')) {
Expand All @@ -49,6 +159,7 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
}

// fix bundledDependencies typo
// normalize bundleDependencies
if (steps.includes('bundledDependencies')) {
if (data.bundleDependencies === undefined && data.bundledDependencies !== undefined) {
data.bundleDependencies = data.bundledDependencies
Expand All @@ -70,7 +181,7 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
changes?.push(`"bundleDependencies" was changed from an object to an array`)
data.bundleDependencies = Object.keys(bd)
}
} else {
} else if ('bundleDependencies' in data) {
changes?.push(`"bundleDependencies" was removed`)
delete data.bundleDependencies
}
Expand All @@ -84,11 +195,11 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
if (data.dependencies &&
data.optionalDependencies && typeof data.optionalDependencies === 'object') {
for (const name in data.optionalDependencies) {
changes?.push(`optionalDependencies entry "${name}" was removed`)
changes?.push(`optionalDependencies."${name}" was removed`)
delete data.dependencies[name]
}
if (!Object.keys(data.dependencies).length) {
changes?.push(`empty "optionalDependencies" was removed`)
changes?.push(`Empty "optionalDependencies" was removed`)
delete data.dependencies
}
}
Expand Down Expand Up @@ -121,20 +232,21 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
}

// strip "node_modules/.bin" from scripts entries
// remove invalid scripts entries (non-strings)
if (steps.includes('scripts') || steps.includes('scriptpath')) {
const spre = /^(\.[/\\])?node_modules[/\\].bin[\\/]/
if (typeof data.scripts === 'object') {
for (const name in data.scripts) {
if (typeof data.scripts[name] !== 'string') {
delete data.scripts[name]
changes?.push(`invalid scripts entry "${name}" was removed`)
} else if (steps.includes('scriptpath')) {
changes?.push(`Invalid scripts."${name}" was removed`)
} else if (steps.includes('scriptpath') && spre.test(data.scripts[name])) {
data.scripts[name] = data.scripts[name].replace(spre, '')
changes?.push(`scripts entry "${name}" was fixed to remove node_modules/.bin reference`)
}
}
} else {
changes?.push(`removed invalid "scripts"`)
changes?.push(`Removed invalid "scripts"`)
delete data.scripts
}
}
Expand All @@ -154,7 +266,7 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
.map(line => line.replace(/^\s*#.*$/, '').trim())
.filter(line => line)
data.contributors = authors
changes.push('"contributors" was auto-populated with the contents of the "AUTHORS" file')
changes?.push('"contributors" was auto-populated with the contents of the "AUTHORS" file')
} catch {
// do nothing
}
Expand Down Expand Up @@ -201,7 +313,7 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
}

if (steps.includes('bin') || steps.includes('binDir') || steps.includes('binRefs')) {
normalizePackageBin(data)
normalizePackageBin(data, changes)
}

// expand "directories.bin"
Expand All @@ -216,7 +328,7 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })
return acc
}, {})
// *sigh*
normalizePackageBin(data)
normalizePackageBin(data, changes)
}

// populate "gitHead" attribute
Expand Down Expand Up @@ -320,22 +432,96 @@ const normalize = async (pkg, { strict, steps, root, changes, allowLegacyCase })

// Some steps are isolated so we can do a limited subset of these in `fix`
if (steps.includes('fixRepositoryField') || steps.includes('normalizeData')) {
legacyFixer.fixRepositoryField(data)
}

if (steps.includes('fixBinField') || steps.includes('normalizeData')) {
legacyFixer.fixBinField(data)
if (data.repositories) {
/* eslint-disable-next-line max-len */
changes?.push(`"repository" was set to the first entry in "repositories" (${data.repository})`)
data.repository = data.repositories[0]
}
if (data.repository) {
if (typeof data.repository === 'string') {
changes?.push('"repository" was changed from a string to an object')
data.repository = {
type: 'git',
url: data.repository,
}
}
if (data.repository.url) {
const hosted = hostedGitInfo.fromUrl(data.repository.url)
let r
if (hosted) {
if (hosted.getDefaultRepresentation() === 'shortcut') {
r = hosted.https()
} else {
r = hosted.toString()
}
if (r !== data.repository.url) {
changes?.push(`"repository.url" was normalized to "${r}"`)
data.repository.url = r
}
}
}
}
}

if (steps.includes('fixDependencies') || steps.includes('normalizeData')) {
legacyFixer.fixDependencies(data, strict)
}
// peerDependencies?
// devDependencies is meaningless here, it's ignored on an installed package
for (const type of ['dependencies', 'devDependencies', 'optionalDependencies']) {
if (data[type]) {
let secondWarning = true
if (typeof data[type] === 'string') {
changes?.push(`"${type}" was converted from a string into an object`)
data[type] = data[type].trim().split(/[\n\r\s\t ,]+/)
secondWarning = false
}
if (Array.isArray(data[type])) {
if (secondWarning) {
changes?.push(`"${type}" was converted from an array into an object`)
}
const o = {}
for (const d of data[type]) {
if (typeof d === 'string') {
const dep = d.trim().split(/(:?[@\s><=])/)
const dn = dep.shift()
const dv = dep.join('').replace(/^@/, '').trim()
o[dn] = dv
}
}
data[type] = o
}
}
}
// normalize-package-data used to put optional dependencies BACK into
// dependencies here, we no longer do this

if (steps.includes('fixScriptsField') || steps.includes('normalizeData')) {
legacyFixer.fixScriptsField(data)
for (const deps of ['dependencies', 'devDependencies']) {
if (deps in data) {
if (!data[deps] || typeof data[deps] !== 'object') {
changes?.push(`Removed invalid "${deps}"`)
delete data[deps]
} else {
for (const d in data[deps]) {
const r = data[deps][d]
if (typeof r !== 'string') {
changes?.push(`Removed invalid "${deps}.${d}"`)
delete data[deps][d]
}
const hosted = hostedGitInfo.fromUrl(data[deps][d])?.toString()
if (hosted && hosted !== data[deps][d]) {
changes?.push(`Normalized git reference to "${deps}.${d}"`)
data[deps][d] = hosted.toString()
}
}
}
}
}
}

if (steps.includes('normalizeData')) {
legacyFixer.warn = function () {
changes?.push(legacyMakeWarning.apply(null, arguments))
}

const legacySteps = [
'fixDescriptionField',
'fixModulesField',
Expand Down
Loading

0 comments on commit a0763d3

Please sign in to comment.