diff --git a/os/env/path-extender-posix/path-extender-posix.spec.ts b/os/env/path-extender-posix/path-extender-posix.spec.ts index c512621..09d77b5 100644 --- a/os/env/path-extender-posix/path-extender-posix.spec.ts +++ b/os/env/path-extender-posix/path-extender-posix.spec.ts @@ -1147,3 +1147,175 @@ end # pnpm end`) }) }) + +describe('Nushell', () => { + let configFile!: string + beforeAll(() => { + process.env.NU_VERSION = '0.92.2' + }) + beforeEach(() => { + configFile = path.join(homeDir, '.config/nushell/env.nu') + }) + it('should append to empty shell script', async () => { + fs.mkdirSync('.config/nushell', { recursive: true }) + fs.writeFileSync(configFile, '', 'utf8') + const report = await addDirToPosixEnvPath(pnpmHomeDir, { + proxyVarName: 'PNPM_HOME', + configSectionName: 'pnpm', + }) + expect(report).toStrictEqual({ + configFile: { + path: configFile, + changeType: 'appended', + }, + oldSettings: ``, + newSettings: `$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME )`, + }) + const configContent = fs.readFileSync(configFile, 'utf8') + expect(configContent).toEqual(` +# pnpm +$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME ) +# pnpm end +`) + }) + it('should append to empty shell script without using a proxy varialbe', async () => { + fs.mkdirSync('.config/nushell', { recursive: true }) + fs.writeFileSync(configFile, '', 'utf8') + const report = await addDirToPosixEnvPath(pnpmHomeDir, { + configSectionName: 'pnpm', + }) + expect(report).toStrictEqual({ + configFile: { + path: configFile, + changeType: 'appended', + }, + oldSettings: ``, + newSettings: `$env.PATH = ($env.PATH | split row (char esep) | prepend ${pnpmHomeDir} )`, + }) + const configContent = fs.readFileSync(configFile, 'utf8') + expect(configContent).toEqual(` +# pnpm +$env.PATH = ($env.PATH | split row (char esep) | prepend ${pnpmHomeDir} ) +# pnpm end +`) + }) + it('should add the new dir to the end of PATH', async () => { + fs.mkdirSync('.config/nushell', { recursive: true }) + fs.writeFileSync(configFile, '', 'utf8') + const report = await addDirToPosixEnvPath(pnpmHomeDir, { + proxyVarName: 'PNPM_HOME', + configSectionName: 'pnpm', + position: 'end', + }) + expect(report).toStrictEqual({ + configFile: { + path: configFile, + changeType: 'appended', + }, + oldSettings: ``, + newSettings: `$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | append $env.PNPM_HOME )`, + }) + const configContent = fs.readFileSync(configFile, 'utf8') + expect(configContent).toEqual(` +# pnpm +$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | append $env.PNPM_HOME ) +# pnpm end +`) + }) + it('should create a shell script', async () => { + const report = await addDirToPosixEnvPath(pnpmHomeDir, { + proxyVarName: 'PNPM_HOME', + configSectionName: 'pnpm', + }) + expect(report).toStrictEqual({ + configFile: { + path: configFile, + changeType: 'created', + }, + oldSettings: ``, + newSettings: `$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME )` + }) + const configContent = fs.readFileSync(configFile, 'utf8') + expect(configContent).toEqual(`# pnpm +$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME ) +# pnpm end +`) + }) + it('should make no changes to a shell script that already has the necessary configurations', async () => { + fs.mkdirSync('.config/nushell', { recursive: true }) + fs.writeFileSync(configFile, ` +# pnpm +$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME ) +# pnpm end`, 'utf8') + const report = await addDirToPosixEnvPath(pnpmHomeDir, { + proxyVarName: 'PNPM_HOME', + configSectionName: 'pnpm', + }) + expect(report).toStrictEqual({ + configFile: { + path: configFile, + changeType: 'skipped', + }, + oldSettings: `$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME )`, + newSettings: `$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME )` + }) + const configContent = fs.readFileSync(configFile, 'utf8') + expect(configContent).toEqual(` +# pnpm +$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME ) +# pnpm end`) + }) + it('should fail if the shell already has PNPM_HOME set to a different directory', async () => { + fs.mkdirSync('.config/nushell', { recursive: true }) + fs.writeFileSync(configFile, ` +# pnpm +$env.PNPM_HOME = "pnpm_home" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME ) +# pnpm end`, 'utf8') + await expect( + addDirToPosixEnvPath(pnpmHomeDir, { + proxyVarName: 'PNPM_HOME', + configSectionName: 'pnpm', + }) + ).rejects.toThrowError(/The config file at/) + }) + it('should not fail if setup is forced', async () => { + fs.mkdirSync('.config/nushell', { recursive: true }) + fs.writeFileSync(configFile, ` +# pnpm +$env.PNPM_HOME = "pnpm_home" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME ) +# pnpm end`, 'utf8') + const report = await addDirToPosixEnvPath(pnpmHomeDir, { + proxyVarName: 'PNPM_HOME', + overwrite: true, + configSectionName: 'pnpm', + }) + expect(report).toStrictEqual({ + configFile: { + path: configFile, + changeType: 'modified', + }, + oldSettings: `$env.PNPM_HOME = "pnpm_home" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME )`, + newSettings: `$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME )` + }) + const configContent = fs.readFileSync(configFile, 'utf8') + expect(configContent).toEqual(` +# pnpm +$env.PNPM_HOME = "${pnpmHomeDir}" +$env.PATH = ($env.PATH | split row (char esep) | prepend $env.PNPM_HOME ) +# pnpm end`) + }) +}) diff --git a/os/env/path-extender-posix/path-extender-posix.ts b/os/env/path-extender-posix/path-extender-posix.ts index b3dec58..e60fc36 100644 --- a/os/env/path-extender-posix/path-extender-posix.ts +++ b/os/env/path-extender-posix/path-extender-posix.ts @@ -22,7 +22,7 @@ export interface AddDirToPosixEnvPathOpts { configSectionName: string } -export type ShellType = 'zsh' | 'bash' | 'fish' | 'ksh' | 'dash' | 'sh' +export type ShellType = 'zsh' | 'bash' | 'fish' | 'ksh' | 'dash' | 'sh' | 'nu' export type ConfigFileChangeType = 'skipped' | 'appended' | 'modified' | 'created' @@ -50,6 +50,7 @@ function detectCurrentShell () { if (process.env.ZSH_VERSION) return 'zsh' if (process.env.BASH_VERSION) return 'bash' if (process.env.FISH_VERSION) return 'fish' + if (process.env.NU_VERSION) return 'nu' return typeof process.env.SHELL === 'string' ? path.basename(process.env.SHELL) : null } @@ -69,8 +70,11 @@ async function updateShell ( case 'fish': { return setupFishShell(pnpmHomeDir, opts) } + case 'nu': { + return setupNuShell(pnpmHomeDir, opts) } - const supportedShellsMsg = 'Supported shell languages are bash, zsh, fish, ksh, dash, and sh.' + } + const supportedShellsMsg = 'Supported shell languages are bash, zsh, fish, ksh, dash, sh, and nushell.' if (!currentShell) throw new PnpmError('UNKNOWN_SHELL', 'Could not infer shell type.', { hint: `Set the SHELL environment variable to your active shell. ${supportedShellsMsg}` @@ -158,6 +162,27 @@ end` } } +async function setupNuShell (dir: string, opts: AddDirToPosixEnvPathOpts): Promise { + const configFile = path.join(os.homedir(), '.config/nushell/env.nu') + let newSettings!: string + const addingCommand = (opts.position ?? "start") === "start" ? "prepend" : "append" + if (opts.proxyVarName) { + newSettings = `$env.${opts.proxyVarName} = "${dir}" +$env.PATH = ($env.PATH | split row (char esep) | ${addingCommand} $env.${opts.proxyVarName} )` + } else { + newSettings = `$env.PATH = ($env.PATH | split row (char esep) | ${addingCommand} ${dir} )` + } + const content = wrapSettings(opts.configSectionName, newSettings) + const { changeType, oldSettings } = await updateShellConfig(configFile, content, opts) + return { + configFile: { + path: configFile, + changeType, + }, + oldSettings, + newSettings, + } +} function wrapSettings (sectionName: string, settings: string): string { return `# ${sectionName} ${settings}