mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-25 10:31:55 -04:00
99 lines
3.8 KiB
TypeScript
99 lines
3.8 KiB
TypeScript
import fs from 'fs'
|
|
import path from 'path'
|
|
import { docsUrl } from '@pnpm/cli-utils'
|
|
import { types as allTypes } from '@pnpm/config'
|
|
import { install } from '@pnpm/plugin-commands-installation'
|
|
import { fromDir as readPackageJsonFromDir } from '@pnpm/read-package-json'
|
|
import { tryReadProjectManifest } from '@pnpm/read-project-manifest'
|
|
import pick from 'ramda/src/pick'
|
|
import execa from 'safe-execa'
|
|
import escapeStringRegexp from 'escape-string-regexp'
|
|
import renderHelp from 'render-help'
|
|
|
|
export const rcOptionsTypes = cliOptionsTypes
|
|
|
|
export function cliOptionsTypes () {
|
|
return pick([], allTypes)
|
|
}
|
|
|
|
export const commandNames = ['patch-commit']
|
|
|
|
export function help () {
|
|
return renderHelp({
|
|
description: 'Generate a patch out of a directory',
|
|
descriptionLists: [],
|
|
url: docsUrl('patch-commit'),
|
|
usages: ['pnpm patch-commit <patchDir>'],
|
|
})
|
|
}
|
|
|
|
export async function handler (opts: install.InstallCommandOptions, params: string[]) {
|
|
const userDir = params[0]
|
|
const srcDir = path.join(userDir, '../source')
|
|
const patchContent = await diffFolders(srcDir, userDir)
|
|
const lockfileDir = opts.lockfileDir ?? opts.dir ?? process.cwd()
|
|
const patchesDir = path.join(lockfileDir, 'patches')
|
|
await fs.promises.mkdir(patchesDir, { recursive: true })
|
|
const patchedPkgManifest = await readPackageJsonFromDir(srcDir)
|
|
const pkgNameAndVersion = `${patchedPkgManifest.name}@${patchedPkgManifest.version}`
|
|
const patchFileName = pkgNameAndVersion.replace('/', '__')
|
|
await fs.promises.writeFile(path.join(patchesDir, `${patchFileName}.patch`), patchContent, 'utf8')
|
|
let { manifest, writeProjectManifest } = await tryReadProjectManifest(lockfileDir)
|
|
if (!manifest) {
|
|
manifest = {}
|
|
}
|
|
if (!manifest.pnpm) {
|
|
manifest.pnpm = {
|
|
patchedDependencies: {},
|
|
}
|
|
} else if (!manifest.pnpm.patchedDependencies) {
|
|
manifest.pnpm.patchedDependencies = {}
|
|
}
|
|
manifest.pnpm.patchedDependencies![pkgNameAndVersion] = `patches/${patchFileName}.patch`
|
|
await writeProjectManifest(manifest)
|
|
return install.handler(opts)
|
|
}
|
|
|
|
async function diffFolders (folderA: string, folderB: string) {
|
|
const folderAN = folderA.replace(/\\/g, '/')
|
|
const folderBN = folderB.replace(/\\/g, '/')
|
|
let stdout!: string
|
|
let stderr!: string
|
|
|
|
try {
|
|
const result = await execa('git', ['-c', 'core.safecrlf=false', 'diff', '--src-prefix=a/', '--dst-prefix=b/', '--ignore-cr-at-eol', '--irreversible-delete', '--full-index', '--no-index', '--text', folderAN, folderBN], {
|
|
cwd: process.cwd(),
|
|
env: {
|
|
...process.env,
|
|
// #region Predictable output
|
|
// These variables aim to ignore the global git config so we get predictable output
|
|
// https://git-scm.com/docs/git#Documentation/git.txt-codeGITCONFIGNOSYSTEMcode
|
|
GIT_CONFIG_NOSYSTEM: '1',
|
|
HOME: '',
|
|
XDG_CONFIG_HOME: '',
|
|
USERPROFILE: '',
|
|
// #endregion
|
|
},
|
|
})
|
|
stdout = result.stdout
|
|
stderr = result.stderr
|
|
} catch (err: any) { // eslint-disable-line
|
|
stdout = err.stdout
|
|
stderr = err.stderr
|
|
}
|
|
// we cannot rely on exit code, because --no-index implies --exit-code
|
|
// i.e. git diff will exit with 1 if there were differences
|
|
if (stderr.length > 0)
|
|
throw new Error(`Unable to diff directories. Make sure you have a recent version of 'git' available in PATH.\nThe following error was reported by 'git':\n${stderr}`)
|
|
|
|
const normalizePath = folderAN.startsWith('/')
|
|
? (p: string) => p.slice(1)
|
|
: (p: string) => p
|
|
|
|
return stdout
|
|
.replace(new RegExp(`(a|b)(${escapeStringRegexp(`/${normalizePath(folderAN)}/`)})`, 'g'), '$1/')
|
|
.replace(new RegExp(`(a|b)${escapeStringRegexp(`/${normalizePath(folderBN)}/`)}`, 'g'), '$1/')
|
|
.replace(new RegExp(escapeStringRegexp(`${folderAN}/`), 'g'), '')
|
|
.replace(new RegExp(escapeStringRegexp(`${folderBN}/`), 'g'), '')
|
|
}
|