This commit is contained in:
2025-08-18 23:06:34 +08:00
parent 0bc04fb659
commit ed18af0cad
1926 changed files with 275098 additions and 0 deletions

118
package/node_modules/libnpmdiff/lib/format-diff.js generated vendored Normal file
View File

@@ -0,0 +1,118 @@
const jsDiff = require('diff')
const shouldPrintPatch = require('./should-print-patch.js')
const colors = {
// red
removed: { open: '\x1B[31m', close: '\x1B[39m' },
// green
added: { open: '\x1B[32m', close: '\x1B[39m' },
// blue
header: { open: '\x1B[34m', close: '\x1B[39m' },
// cyan
section: { open: '\x1B[36m', close: '\x1B[39m' },
}
const color = (colorStr, colorId) => {
const { open, close } = colors[colorId]
// avoid highlighting the "\n" (would highlight till the end of the line)
return colorStr.replace(/[^\n\r]+/g, open + '$&' + close)
}
const formatDiff = ({ files, opts = {}, refs, versions }) => {
let res = ''
const srcPrefix = opts.diffNoPrefix ? '' : opts.diffSrcPrefix || 'a/'
const dstPrefix = opts.diffNoPrefix ? '' : opts.diffDstPrefix || 'b/'
for (const filename of files.values()) {
const names = {
a: `${srcPrefix}${filename}`,
b: `${dstPrefix}${filename}`,
}
let fileMode = ''
const filenames = {
a: refs.get(`a/${filename}`),
b: refs.get(`b/${filename}`),
}
const contents = {
a: filenames.a && filenames.a.content,
b: filenames.b && filenames.b.content,
}
const modes = {
a: filenames.a && filenames.a.mode,
b: filenames.b && filenames.b.mode,
}
if (contents.a === contents.b && modes.a === modes.b) {
continue
}
if (opts.diffNameOnly) {
res += `${filename}\n`
continue
}
let patch = ''
let headerLength = 0
const header = str => {
headerLength++
patch += `${str}\n`
}
// manually build a git diff-compatible header
header(`diff --git ${names.a} ${names.b}`)
if (modes.a === modes.b) {
fileMode = filenames.a.mode
} else {
if (modes.a && !modes.b) {
header(`deleted file mode ${modes.a}`)
} else if (!modes.a && modes.b) {
header(`new file mode ${modes.b}`)
} else {
header(`old mode ${modes.a}`)
header(`new mode ${modes.b}`)
}
}
/* eslint-disable-next-line max-len */
header(`index ${opts.tagVersionPrefix || 'v'}${versions.a}..${opts.tagVersionPrefix || 'v'}${versions.b} ${fileMode}`)
if (shouldPrintPatch(filename)) {
patch += jsDiff.createTwoFilesPatch(
names.a,
names.b,
contents.a || '',
contents.b || '',
'',
'',
{
context: opts.diffUnified === 0 ? 0 : opts.diffUnified || 3,
ignoreWhitespace: opts.diffIgnoreAllSpace,
}
).replace(
'===================================================================\n',
''
).replace(/\t\n/g, '\n') // strip trailing tabs
headerLength += 2
} else {
header(`--- ${names.a}`)
header(`+++ ${names.b}`)
}
if (opts.color) {
// this RegExp will include all the `\n` chars into the lines, easier to join
const lines = patch.split(/^/m)
res += color(lines.slice(0, headerLength).join(''), 'header')
res += lines.slice(headerLength).join('')
.replace(/^-.*/gm, color('$&', 'removed'))
.replace(/^\+.*/gm, color('$&', 'added'))
.replace(/^@@.+@@/gm, color('$&', 'section'))
} else {
res += patch
}
}
return res.trim()
}
module.exports = formatDiff

62
package/node_modules/libnpmdiff/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,62 @@
const pacote = require('pacote')
const formatDiff = require('./format-diff.js')
const getTarball = require('./tarball.js')
const untar = require('./untar.js')
// TODO: we test this condition in the diff command
// so this error probably doesnt need to be here. Or
// if it does we should figure out a standard code
// so we can catch it in the cli and display it consistently
const argsError = () =>
Object.assign(
new TypeError('libnpmdiff needs two arguments to compare'),
{ code: 'EDIFFARGS' }
)
const diff = async (specs, opts = {}) => {
if (specs.length !== 2) {
throw argsError()
}
const [
aManifest,
bManifest,
] =
await Promise.all(specs.map(spec => pacote.manifest(spec, opts)))
const versions = {
a: aManifest.version,
b: bManifest.version,
}
// fetches tarball using pacote
const [a, b] = await Promise.all([
getTarball(aManifest, opts),
getTarball(bManifest, opts),
])
// read all files
// populates `files` and `refs`
const {
files,
refs,
} = await untar([
{
prefix: 'a/',
item: a,
},
{
prefix: 'b/',
item: b,
},
], opts)
return formatDiff({
files,
opts,
refs,
versions,
})
}
module.exports = diff

View File

@@ -0,0 +1,22 @@
const { basename, extname } = require('node:path')
const binaryExtensions = require('binary-extensions')
// we should try to print patches as long as the
// extension is not identified as binary files
const shouldPrintPatch = (path, opts = {}) => {
if (opts.diffText) {
return true
}
const filename = basename(path)
const extension = (
filename.startsWith('.')
? filename
: extname(filename)
).slice(1)
return !binaryExtensions.includes(extension)
}
module.exports = shouldPrintPatch

38
package/node_modules/libnpmdiff/lib/tarball.js generated vendored Normal file
View File

@@ -0,0 +1,38 @@
const { relative } = require('node:path')
const Arborist = require('@npmcli/arborist')
const npa = require('npm-package-arg')
const pkgContents = require('@npmcli/installed-package-contents')
const pacote = require('pacote')
const { tarCreateOptions } = pacote.DirFetcher
const tar = require('tar')
// returns a simplified tarball when reading files from node_modules folder,
// thus avoiding running the prepare scripts and the extra logic from packlist
const nodeModulesTarball = (manifest) =>
pkgContents({ path: manifest._resolved, depth: 1 })
.then(files =>
files.map(file => relative(manifest._resolved, file))
)
.then(files =>
tar.c(tarCreateOptions(manifest), files).concat()
)
const tarball = (manifest, opts) => {
const resolved = manifest._resolved
const where = opts.where || process.cwd()
const fromNodeModules = npa(resolved).type === 'directory'
&& /node_modules[\\/](@[^\\/]+\/)?[^\\/]+[\\/]?$/.test(relative(where, resolved))
if (fromNodeModules) {
return nodeModulesTarball(manifest, opts)
}
return pacote.tarball(manifest._resolved, {
...opts,
Arborist,
})
}
module.exports = tarball

96
package/node_modules/libnpmdiff/lib/untar.js generated vendored Normal file
View File

@@ -0,0 +1,96 @@
const tar = require('tar')
const { minimatch } = require('minimatch')
const normalizeMatch = str => str
.replace(/\\+/g, '/')
.replace(/^\.\/|^\./, '')
// files and refs are mutating params
// filterFiles, item, prefix and opts are read-only options
const untar = ({ files, refs }, { filterFiles, item, prefix }) => {
tar.list({
filter: (path, entry) => {
const fileMatch = () =>
(!filterFiles.length ||
filterFiles.some(f => {
const pattern = normalizeMatch(f)
return minimatch(
normalizeMatch(path),
`{package/,}${pattern}`,
{ matchBase: pattern.startsWith('*') }
)
}))
// expands usage of simple path filters, e.g: lib or src/
const folderMatch = () =>
filterFiles.some(f =>
normalizeMatch(path).startsWith(normalizeMatch(f)) ||
normalizeMatch(path).startsWith(`package/${normalizeMatch(f)}`))
if (
entry.type === 'File' &&
(fileMatch() || folderMatch())
) {
const key = path.replace(/^[^/]+\/?/, '')
files.add(key)
// should skip reading file when using --name-only option
let content
try {
entry.setEncoding('utf8')
content = entry.concat()
} catch (e) {
/* istanbul ignore next */
throw Object.assign(
new Error('failed to read files'),
{ code: 'EDIFFUNTAR' }
)
}
refs.set(`${prefix}${key}`, {
content,
mode: `100${entry.mode.toString(8)}`,
})
return true
}
},
})
.on('error', /* istanbul ignore next */ e => {
throw e
})
.end(item)
}
const readTarballs = async (tarballs, opts = {}) => {
const files = new Set()
const refs = new Map()
const arr = [].concat(tarballs)
const filterFiles = opts.diffFiles || []
for (const i of arr) {
untar({
files,
refs,
}, {
item: i.item,
prefix: i.prefix,
filterFiles,
})
}
// await to read all content from included files
const allRefs = [...refs.values()]
const contents = await Promise.all(allRefs.map(async ref => ref.content))
contents.forEach((content, index) => {
allRefs[index].content = content
})
return {
files,
refs,
}
}
module.exports = readTarballs