Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: pnpm setup #4713

Merged
merged 5 commits into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/plugin-commands-setup/src/BadHomeDirError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import PnpmError from '@pnpm/error'

export class BadHomeDirError extends PnpmError {
constructor ({ wantedDir, currentDir }: { wantedDir: string, currentDir: string }) {
super('DIFFERENT_HOME_DIR_IS_SET', `Currently 'PNPM_HOME' is set to '${currentDir}'`, {
hint: 'If you want to override the existing PNPM_HOME env variable, use the --force option',
})
}
}
57 changes: 42 additions & 15 deletions packages/plugin-commands-setup/src/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { docsUrl } from '@pnpm/cli-utils'
import logger from '@pnpm/logger'
import renderHelp from 'render-help'
import { setupWindowsEnvironmentPath } from './setupOnWindows'
import { BadHomeDirError } from './BadHomeDirError'

export const rcOptionsTypes = () => ({})

Expand Down Expand Up @@ -90,14 +91,14 @@ async function updateShell (
switch (currentShell) {
case 'bash': {
const configFile = path.join(os.homedir(), '.bashrc')
return setupShell(configFile, pnpmHomeDir)
return setupShell(configFile, pnpmHomeDir, opts)
}
case 'zsh': {
const configFile = path.join(os.homedir(), '.zshrc')
return setupShell(configFile, pnpmHomeDir)
return setupShell(configFile, pnpmHomeDir, opts)
}
case 'fish': {
return setupFishShell(pnpmHomeDir)
return setupFishShell(pnpmHomeDir, opts)
}
}

Expand All @@ -108,35 +109,61 @@ async function updateShell (
return 'Could not infer shell type.'
}

async function setupShell (configFile: string, pnpmHomeDir: string): Promise<string> {
const content = `export PNPM_HOME="${pnpmHomeDir}"
async function setupShell (configFile: string, pnpmHomeDir: string, opts: { force: boolean }): Promise<string> {
const content = `# pnpm
export PNPM_HOME="${pnpmHomeDir}"
export PATH="$PNPM_HOME:$PATH"
# pnpm end
`
if (!fs.existsSync(configFile)) {
await fs.promises.writeFile(configFile, content, 'utf8')
return `Created ${configFile}`
}
const configContent = await fs.promises.readFile(configFile, 'utf8')
if (configContent.includes('PNPM_HOME')) {
return `PNPM_HOME is already in ${configFile}`
if (!configContent.includes('PNPM_HOME')) {
await fs.promises.appendFile(configFile, `\n${content}`, 'utf8')
return `Updated ${configFile}`
}
await fs.promises.appendFile(configFile, `\n${content}`, 'utf8')
return `Updated ${configFile}`
const match = configContent.match(/export PNPM_HOME="(.*)"/)
if (match && match[1] !== pnpmHomeDir) {
if (!opts.force) {
throw new BadHomeDirError({ currentDir: match[1], wantedDir: pnpmHomeDir })
}
const newConfigContent = replaceSection(configContent, content)
await fs.promises.writeFile(configFile, newConfigContent, 'utf8')
return `Updated ${configFile}`
}
return `PNPM_HOME is already in ${configFile}`
}

async function setupFishShell (pnpmHomeDir: string): Promise<string> {
async function setupFishShell (pnpmHomeDir: string, opts: { force: boolean }): Promise<string> {
const configFile = path.join(os.homedir(), '.config/fish/config.fish')
const content = `set -gx PNPM_HOME "${pnpmHomeDir}"
const content = `# pnpm
set -gx PNPM_HOME "${pnpmHomeDir}"
set -gx PATH "$PNPM_HOME" $PATH
# pnpm end
`
if (!fs.existsSync(configFile)) {
await fs.promises.writeFile(configFile, content, 'utf8')
return `Created ${configFile}`
}
const configContent = await fs.promises.readFile(configFile, 'utf8')
if (configContent.includes('PNPM_HOME')) {
return `PNPM_HOME is already in ${configFile}`
if (!configContent.includes('PNPM_HOME')) {
await fs.promises.appendFile(configFile, `\n${content}`, 'utf8')
return `Updated ${configFile}`
}
const match = configContent.match(/set -gx PNPM_HOME "(.*)"/)
if (match && match[1] !== pnpmHomeDir) {
if (!opts.force) {
throw new BadHomeDirError({ currentDir: match[1], wantedDir: pnpmHomeDir })
}
const newConfigContent = replaceSection(configContent, content)
await fs.promises.writeFile(configFile, newConfigContent, 'utf8')
return `Updated ${configFile}`
}
await fs.promises.appendFile(configFile, `\n${content}`, 'utf8')
return `Updated ${configFile}`
return `PNPM_HOME is already in ${configFile}`
}

function replaceSection (originalContent: string, newSection: string): string {
return originalContent.replace(/# pnpm[\s\S]*# pnpm end/g, newSection)
}
24 changes: 10 additions & 14 deletions packages/plugin-commands-setup/src/setupOnWindows.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import PnpmError from '@pnpm/error'
import { win32 as path } from 'path'
import execa from 'execa'
import { BadHomeDirError } from './BadHomeDirError'

type IEnvironmentValueMatch = { groups: { name: string, type: string, data: string } } & RegExpMatchArray

Expand Down Expand Up @@ -28,24 +28,26 @@ function pathIncludesDir (pathValue: string, dir: string): boolean {
}

export async function setupWindowsEnvironmentPath (pnpmHomeDir: string, opts: { force: boolean }): Promise<string> {
pnpmHomeDir = path.normalize(pnpmHomeDir)
// Use `chcp` to make `reg` use utf8 encoding for output.
// Otherwise, the non-ascii characters in the environment variables will become garbled characters.

const chcpResult = await execa('chcp')
const cpMatch = /\d+/.exec(chcpResult.stdout) ?? []
const cpBak = parseInt(cpMatch[0])
if (chcpResult.failed || cpBak === 0) {
if (chcpResult.failed || !(cpBak > 0)) {
return `exec chcp failed: ${cpBak}, ${chcpResult.stderr}`
}

await execa('chcp', ['65001'])
try {
return await _setupWindowsEnvironmentPath(path.normalize(pnpmHomeDir), opts)
} finally {
await execa('chcp', [cpBak.toString()])
}
}

async function _setupWindowsEnvironmentPath (pnpmHomeDir: string, opts: { force: boolean }): Promise<string> {
const queryResult = await execa('reg', ['query', REG_KEY], { windowsHide: false })

if (queryResult.failed) {
await execa('chcp', [cpBak.toString()])

return 'Win32 registry environment values could not be retrieved'
}

Expand All @@ -59,11 +61,7 @@ export async function setupWindowsEnvironmentPath (pnpmHomeDir: string, opts: {
if (homeValueMatch.length === 1 && !opts.force) {
const currentHomeDir = homeValueMatch[0].groups.data
if (currentHomeDir !== pnpmHomeDir) {
await execa('chcp', [cpBak.toString()])

throw new PnpmError('DIFFERENT_HOME_DIR_IS_SET', `Currently 'PNPM_HOME' is set to '${currentHomeDir}'`, {
hint: 'If you want to override the existing PNPM_HOME env variable, use the --force option',
})
throw new BadHomeDirError({ currentDir: currentHomeDir, wantedDir: pnpmHomeDir })
}
} else {
logger.push(`Setting 'PNPM_HOME' to value '${pnpmHomeDir}'`)
Expand Down Expand Up @@ -99,7 +97,5 @@ export async function setupWindowsEnvironmentPath (pnpmHomeDir: string, opts: {
await execa('setx', ['PNPM_HOME', pnpmHomeDir])
}

await execa('chcp', [cpBak.toString()])

return logger.join('\n')
}
Loading