mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
601 lines
19 KiB
TypeScript
601 lines
19 KiB
TypeScript
/// <reference path="../../../__typings__/index.d.ts"/>
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
import { logger, globalWarn } from '@pnpm/logger'
|
|
import {
|
|
linkBins,
|
|
linkBinsOfPackages,
|
|
linkBinsOfPkgsByAliases,
|
|
} from '@pnpm/link-bins'
|
|
import { fixtures } from '@pnpm/test-fixtures'
|
|
import { jest } from '@jest/globals'
|
|
import CMD_EXTENSION from 'cmd-extension'
|
|
import isWindows from 'is-windows'
|
|
import normalizePath from 'normalize-path'
|
|
import tempy from 'tempy'
|
|
import { spawnSync } from 'child_process'
|
|
|
|
jest.mock('@pnpm/logger', () => {
|
|
const debug = jest.fn()
|
|
const globalWarn = jest.fn()
|
|
|
|
return {
|
|
logger: () => ({ debug }),
|
|
globalWarn,
|
|
}
|
|
})
|
|
|
|
const binsConflictLogger = logger('bins-conflict')
|
|
// The fixture directories are copied to before the tests run
|
|
// This happens because the tests convert some of the files into executables
|
|
const f = fixtures(__dirname)
|
|
|
|
beforeEach(() => {
|
|
jest.mocked(binsConflictLogger.debug).mockClear()
|
|
jest.mocked(globalWarn).mockClear()
|
|
})
|
|
|
|
const POWER_SHELL_IS_SUPPORTED = isWindows()
|
|
const IS_WINDOWS = isWindows()
|
|
const EXECUTABLE_SHEBANG_SUPPORTED = !IS_WINDOWS
|
|
|
|
const testOnWindows = IS_WINDOWS ? test : test.skip
|
|
|
|
function getExpectedBins (bins: string[]) {
|
|
const expectedBins = [...bins]
|
|
if (POWER_SHELL_IS_SUPPORTED) {
|
|
bins.forEach((bin) => expectedBins.push(`${bin}.ps1`))
|
|
}
|
|
if (IS_WINDOWS) {
|
|
bins.forEach((bin) => expectedBins.push(`${bin}${CMD_EXTENSION}`))
|
|
}
|
|
return expectedBins.sort()
|
|
}
|
|
|
|
test('linkBins()', async () => {
|
|
const binTarget = tempy.directory()
|
|
const warn = jest.fn()
|
|
const simpleFixture = f.prepare('simple-fixture')
|
|
|
|
await linkBins(path.join(simpleFixture, 'node_modules'), binTarget, { warn })
|
|
|
|
expect(warn).not.toHaveBeenCalled()
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['simple']))
|
|
const binLocation = path.join(binTarget, 'simple')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/simple/index.js')
|
|
|
|
if (EXECUTABLE_SHEBANG_SUPPORTED) {
|
|
const binFile = path.join(binTarget, 'simple')
|
|
const stat = fs.statSync(binFile)
|
|
expect(stat.mode).toBe(parseInt('100755', 8))
|
|
expect(stat.isFile()).toBe(true)
|
|
}
|
|
})
|
|
|
|
test('linkBins() never creates a PowerShell shim for the pnpm CLI', async () => {
|
|
const binTarget = tempy.directory()
|
|
const fixture = f.prepare('pnpm-cli')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(fixture, 'node_modules'), binTarget, { warn })
|
|
|
|
const bins = fs.readdirSync(binTarget)
|
|
expect(bins).toContain('pnpm')
|
|
expect(bins).not.toContain('pnpm.ps1')
|
|
})
|
|
|
|
test('linkBins() finds exotic manifests', async () => {
|
|
const binTarget = tempy.directory()
|
|
const exoticManifestFixture = f.prepare('exotic-manifest')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(exoticManifestFixture, 'node_modules'), binTarget, {
|
|
allowExoticManifests: true,
|
|
warn,
|
|
})
|
|
|
|
expect(warn).not.toHaveBeenCalled()
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['simple']))
|
|
const binLocation = path.join(binTarget, 'simple')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/simple/index.js')
|
|
|
|
if (EXECUTABLE_SHEBANG_SUPPORTED) {
|
|
const binFile = path.join(binTarget, 'simple')
|
|
const stat = fs.statSync(binFile)
|
|
expect(stat.mode).toBe(parseInt('100755', 8))
|
|
expect(stat.isFile()).toBe(true)
|
|
}
|
|
})
|
|
|
|
test('linkBins() do not fail on directory w/o manifest file', async () => {
|
|
const binTarget = tempy.directory()
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(f.find('dir-with-no-manifest/node_modules'), binTarget, {
|
|
allowExoticManifests: false,
|
|
warn,
|
|
})
|
|
|
|
expect(warn).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('linkBins() with exotic manifests do not fail on directory w/o manifest file', async () => {
|
|
const binTarget = tempy.directory()
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(f.find('dir-with-no-manifest/node_modules'), binTarget, {
|
|
allowExoticManifests: true,
|
|
warn,
|
|
})
|
|
|
|
expect(warn).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('linkBins() does not link own bins', async () => {
|
|
const target = f.prepare('foobar')
|
|
|
|
const warn = jest.fn()
|
|
const modules = path.join(target, 'node_modules')
|
|
const binTarget = path.join(target, 'node_modules', 'foo', 'node_modules', '.bin')
|
|
|
|
await linkBins(modules, binTarget, { warn })
|
|
|
|
expect(warn).not.toHaveBeenCalled()
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['bar']))
|
|
})
|
|
|
|
test('linkBinsOfPackages()', async () => {
|
|
const binTarget = tempy.directory()
|
|
const simpleFixture = f.prepare('simple-fixture')
|
|
|
|
await linkBinsOfPackages(
|
|
[
|
|
{
|
|
location: path.join(simpleFixture, 'node_modules/simple'),
|
|
manifest: (await import(path.join(simpleFixture, 'node_modules/simple/package.json'))).default,
|
|
},
|
|
],
|
|
binTarget
|
|
)
|
|
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['simple']))
|
|
const binLocation = path.join(binTarget, 'simple')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/simple/index.js')
|
|
})
|
|
|
|
test('linkBinsOfPkgsByAliases()', async () => {
|
|
const binTarget = tempy.directory()
|
|
const simpleFixture = f.prepare('simple-fixture')
|
|
|
|
await linkBinsOfPkgsByAliases(
|
|
[],
|
|
binTarget,
|
|
{
|
|
modulesDir: path.join(simpleFixture, 'node_modules'),
|
|
warn: () => {},
|
|
}
|
|
)
|
|
expect(fs.readdirSync(binTarget)).toEqual([])
|
|
|
|
await linkBinsOfPkgsByAliases(
|
|
['simple'],
|
|
binTarget,
|
|
{
|
|
modulesDir: path.join(simpleFixture, 'node_modules'),
|
|
warn: () => {},
|
|
}
|
|
)
|
|
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['simple']))
|
|
const binLocation = path.join(binTarget, 'simple')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/simple/index.js')
|
|
})
|
|
|
|
test('linkBins() resolves conflicts. Prefer packages that use their name as bin name', async () => {
|
|
const binTarget = tempy.directory()
|
|
const binNameConflictsFixture = f.prepare('bin-name-conflicts')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(binNameConflictsFixture, 'node_modules'), binTarget, { warn })
|
|
|
|
expect(binsConflictLogger.debug).toHaveBeenCalledWith({
|
|
binaryName: 'bar',
|
|
binsDir: binTarget,
|
|
linkedPkgName: 'bar',
|
|
linkedPkgVersion: expect.any(String),
|
|
skippedPkgName: 'foo',
|
|
skippedPkgVersion: expect.any(String),
|
|
})
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['bar', 'foofoo']))
|
|
|
|
{
|
|
const binLocation = path.join(binTarget, 'bar')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/bar/index.js')
|
|
}
|
|
|
|
{
|
|
const binLocation = path.join(binTarget, 'foofoo')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/foo/index.js')
|
|
}
|
|
})
|
|
|
|
test('linkBins() resolves conflicts. Prefer packages whose name is greater in localeCompare', async () => {
|
|
const binTarget = tempy.directory()
|
|
const binNameConflictsFixture = f.prepare('bin-name-conflicts-no-own-name')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(binNameConflictsFixture, 'node_modules'), binTarget, { warn })
|
|
|
|
expect(binsConflictLogger.debug).toHaveBeenCalledWith({
|
|
binaryName: 'my-command',
|
|
binsDir: binTarget,
|
|
linkedPkgName: 'foo',
|
|
linkedPkgVersion: expect.any(String),
|
|
skippedPkgName: 'bar',
|
|
skippedPkgVersion: expect.any(String),
|
|
})
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['my-command']))
|
|
|
|
{
|
|
const binLocation = path.join(binTarget, 'my-command')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/foo/index.js')
|
|
}
|
|
})
|
|
|
|
test('linkBins() resolves conflicts. Prefer the latest version of the same package', async () => {
|
|
const binTarget = tempy.directory()
|
|
const binNameConflictsFixture = f.prepare('different-versions')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(binNameConflictsFixture, 'node_modules'), binTarget, { warn })
|
|
|
|
expect(binsConflictLogger.debug).toHaveBeenCalledWith({
|
|
binaryName: 'my-command',
|
|
binsDir: binTarget,
|
|
linkedPkgName: 'my-command',
|
|
linkedPkgVersion: expect.any(String),
|
|
skippedPkgName: 'my-command',
|
|
skippedPkgVersion: '1.0.0',
|
|
})
|
|
expect(binsConflictLogger.debug).toHaveBeenCalledWith({
|
|
binaryName: 'my-command',
|
|
binsDir: binTarget,
|
|
linkedPkgName: 'my-command',
|
|
linkedPkgVersion: expect.any(String),
|
|
skippedPkgName: 'my-command',
|
|
skippedPkgVersion: '1.1.0',
|
|
})
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['my-command']))
|
|
|
|
{
|
|
const binLocation = path.join(binTarget, 'my-command')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/my-command-greater/index.js')
|
|
}
|
|
})
|
|
|
|
test('linkBinsOfPackages() resolves conflicts. Prefer packages that use their name as bin name', async () => {
|
|
const binTarget = tempy.directory()
|
|
const binNameConflictsFixture = f.prepare('bin-name-conflicts')
|
|
|
|
const modulesPath = path.join(binNameConflictsFixture, 'node_modules')
|
|
|
|
await linkBinsOfPackages(
|
|
[
|
|
{
|
|
location: path.join(modulesPath, 'bar'),
|
|
manifest: (await import(path.join(modulesPath, 'bar', 'package.json'))).default,
|
|
},
|
|
{
|
|
location: path.join(modulesPath, 'foo'),
|
|
manifest: (await import(path.join(modulesPath, 'foo', 'package.json'))).default,
|
|
},
|
|
],
|
|
binTarget
|
|
)
|
|
|
|
expect(binsConflictLogger.debug).toHaveBeenCalledWith({
|
|
binaryName: 'bar',
|
|
binsDir: binTarget,
|
|
linkedPkgAlias: undefined,
|
|
linkedPkgName: 'bar',
|
|
linkedPkgVersion: expect.any(String),
|
|
skippedPkgAlias: undefined,
|
|
skippedPkgName: 'foo',
|
|
skippedPkgVersion: expect.any(String),
|
|
})
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['bar', 'foofoo']))
|
|
|
|
{
|
|
const binLocation = path.join(binTarget, 'bar')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/bar/index.js')
|
|
}
|
|
|
|
{
|
|
const binLocation = path.join(binTarget, 'foofoo')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/foo/index.js')
|
|
}
|
|
})
|
|
|
|
test('linkBinsOfPackages() resolves conflicts. Prefer the latest version', async () => {
|
|
const binTarget = tempy.directory()
|
|
const binNameConflictsFixture = f.prepare('different-versions')
|
|
|
|
const modulesPath = path.join(binNameConflictsFixture, 'node_modules')
|
|
|
|
await linkBinsOfPackages(
|
|
[
|
|
{
|
|
location: path.join(modulesPath, 'my-command-lesser'),
|
|
manifest: (await import(path.join(modulesPath, 'my-command-lesser', 'package.json'))).default,
|
|
},
|
|
{
|
|
location: path.join(modulesPath, 'my-command-middle'),
|
|
manifest: (await import(path.join(modulesPath, 'my-command-middle', 'package.json'))).default,
|
|
},
|
|
{
|
|
location: path.join(modulesPath, 'my-command-greater'),
|
|
manifest: (await import(path.join(modulesPath, 'my-command-greater', 'package.json'))).default,
|
|
},
|
|
],
|
|
binTarget
|
|
)
|
|
|
|
expect(binsConflictLogger.debug).toHaveBeenCalledWith({
|
|
binaryName: 'my-command',
|
|
binsDir: binTarget,
|
|
linkedPkgAlias: undefined,
|
|
linkedPkgName: 'my-command',
|
|
linkedPkgVersion: expect.any(String),
|
|
skippedPkgAlias: undefined,
|
|
skippedPkgName: 'my-command',
|
|
skippedPkgVersion: '1.0.0',
|
|
})
|
|
expect(binsConflictLogger.debug).toHaveBeenCalledWith({
|
|
binaryName: 'my-command',
|
|
binsDir: binTarget,
|
|
linkedPkgAlias: undefined,
|
|
linkedPkgName: 'my-command',
|
|
linkedPkgVersion: expect.any(String),
|
|
skippedPkgAlias: undefined,
|
|
skippedPkgName: 'my-command',
|
|
skippedPkgVersion: '1.1.0',
|
|
})
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['my-command']))
|
|
|
|
{
|
|
const binLocation = path.join(binTarget, 'my-command')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/my-command-greater/index.js')
|
|
}
|
|
})
|
|
|
|
test('linkBins() resolves conflicts. Prefer packages are direct dependencies', async () => {
|
|
const binTarget = tempy.directory()
|
|
const binNameConflictsFixture = f.prepare('bin-name-conflicts')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(binNameConflictsFixture, 'node_modules'), binTarget, {
|
|
projectManifest: {
|
|
dependencies: {
|
|
foo: '1.0.0',
|
|
},
|
|
},
|
|
warn,
|
|
})
|
|
|
|
expect(warn).not.toHaveBeenCalled() // With(`Cannot link binary 'bar' of 'foo' to '${binTarget}': binary of 'bar' is already linked`, 'BINARIES_CONFLICT')
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['bar', 'foofoo']))
|
|
|
|
{
|
|
const binLocation = path.join(binTarget, 'bar')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/foo/index.js')
|
|
}
|
|
|
|
{
|
|
const binLocation = path.join(binTarget, 'foofoo')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
expect(content).toMatch('node_modules/foo/index.js')
|
|
}
|
|
})
|
|
|
|
test('linkBins() would throw error if package has no name field', async () => {
|
|
const binTarget = tempy.directory()
|
|
const noNameFixture = f.prepare('no-name')
|
|
const warn = jest.fn()
|
|
|
|
try {
|
|
await linkBins(path.join(noNameFixture, 'node_modules'), binTarget, {
|
|
allowExoticManifests: true,
|
|
warn,
|
|
})
|
|
fail('linkBins should fail when package has no name')
|
|
} catch (err: any) { // eslint-disable-line
|
|
const packagePath = normalizePath(path.join(noNameFixture, 'node_modules/simple'))
|
|
expect(err.message).toEqual(`Package in ${packagePath} must have a name to get bin linked.`)
|
|
expect(err.code).toEqual('ERR_PNPM_INVALID_PACKAGE_NAME')
|
|
expect(warn).not.toHaveBeenCalled()
|
|
}
|
|
})
|
|
|
|
test('linkBins() would give warning if package has no bin field', async () => {
|
|
const binTarget = tempy.directory()
|
|
const noBinFixture = f.prepare('no-bin')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(noBinFixture, 'packages'), binTarget, {
|
|
allowExoticManifests: true,
|
|
warn,
|
|
})
|
|
|
|
const packagePath = normalizePath(path.join(noBinFixture, 'packages/simple'))
|
|
expect(warn).toHaveBeenCalledWith(`Package in ${packagePath} must have a non-empty bin field to get bin linked.`, 'EMPTY_BIN')
|
|
})
|
|
|
|
test('linkBins() would not give warning if package has no bin field but inside node_modules', async () => {
|
|
const binTarget = tempy.directory()
|
|
const noBinFixture = f.prepare('no-bin')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(noBinFixture, 'node_modules'), binTarget, {
|
|
allowExoticManifests: true,
|
|
warn,
|
|
})
|
|
|
|
expect(warn).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('linkBins() links commands from bin directory with a subdirectory', async () => {
|
|
const binTarget = tempy.directory()
|
|
|
|
await linkBins(f.find('bin-dir'), binTarget, { warn: () => {} })
|
|
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['index.js']))
|
|
})
|
|
|
|
test('linkBins() fix window shebang line', async () => {
|
|
const binTarget = tempy.directory()
|
|
const windowShebangFixture = f.prepare('bin-window-shebang')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(windowShebangFixture, 'node_modules'), binTarget, { warn })
|
|
|
|
expect(warn).not.toHaveBeenCalled()
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['crlf', 'lf']))
|
|
|
|
const lfBinLoc = path.join(binTarget, 'lf')
|
|
const crlfBinLoc = path.join(binTarget, 'crlf')
|
|
for (const binLocation of [lfBinLoc, crlfBinLoc]) {
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
}
|
|
|
|
if (EXECUTABLE_SHEBANG_SUPPORTED) {
|
|
const lfFilePath = path.join(windowShebangFixture, 'node_modules', 'crlf/bin/lf.js')
|
|
const crlfFilePath = path.join(windowShebangFixture, 'node_modules', 'crlf/bin/crlf.js')
|
|
|
|
for (const filePath of [lfFilePath, crlfFilePath]) {
|
|
const content = fs.readFileSync(filePath, 'utf8')
|
|
expect(content.startsWith('#!/usr/bin/env node\n')).toBeTruthy()
|
|
}
|
|
|
|
const lfStat = fs.statSync(lfBinLoc)
|
|
const crlfStat = fs.statSync(crlfBinLoc)
|
|
for (const stat of [lfStat, crlfStat]) {
|
|
expect(stat.mode).toBe(parseInt('100755', 8))
|
|
expect(stat.isFile()).toBe(true)
|
|
}
|
|
}
|
|
})
|
|
|
|
test("linkBins() emits global warning when bin points to path that doesn't exist", async () => {
|
|
const binTarget = tempy.directory()
|
|
const binNotExistFixture = f.prepare('bin-not-exist')
|
|
|
|
await linkBins(path.join(binNotExistFixture, 'node_modules'), binTarget, {
|
|
allowExoticManifests: true,
|
|
warn: () => {},
|
|
})
|
|
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins([]))
|
|
expect(
|
|
globalWarn
|
|
).toHaveBeenCalled()
|
|
})
|
|
|
|
testOnWindows('linkBins() should remove an existing .exe file from the target directory', async () => {
|
|
const binTarget = tempy.directory()
|
|
fs.writeFileSync(path.join(binTarget, 'simple.exe'), '', 'utf8')
|
|
const warn = jest.fn()
|
|
const simpleFixture = f.prepare('simple-fixture')
|
|
|
|
await linkBins(path.join(simpleFixture, 'node_modules'), binTarget, { warn })
|
|
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['simple']))
|
|
})
|
|
|
|
test('linkBins() should handle bin field pointing to a directory gracefully', async () => {
|
|
const binTarget = tempy.directory()
|
|
const binIsDirFixture = f.prepare('bin-is-directory')
|
|
const warn = jest.fn()
|
|
|
|
await linkBins(path.join(binIsDirFixture, 'node_modules'), binTarget, { warn })
|
|
|
|
expect(fs.readdirSync(binTarget)).toEqual([])
|
|
expect(globalWarn).toHaveBeenCalled()
|
|
})
|
|
|
|
describe('enable prefer-symlinked-executables', () => {
|
|
test('linkBins()', async () => {
|
|
const binTarget = tempy.directory()
|
|
const warn = jest.fn()
|
|
const simpleFixture = f.prepare('simple-fixture')
|
|
|
|
await linkBins(path.join(simpleFixture, 'node_modules'), binTarget, { warn, preferSymlinkedExecutables: true })
|
|
|
|
expect(warn).not.toHaveBeenCalled()
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['simple']))
|
|
const binLocation = path.join(binTarget, 'simple')
|
|
expect(fs.existsSync(binLocation)).toBe(true)
|
|
const content = fs.readFileSync(binLocation, 'utf8')
|
|
if (IS_WINDOWS) {
|
|
expect(content).toMatch('node_modules/simple/index.js')
|
|
} else {
|
|
expect(content).toMatch('console.log(\'hello_world\')')
|
|
}
|
|
|
|
if (EXECUTABLE_SHEBANG_SUPPORTED) {
|
|
const binFile = path.join(binTarget, 'simple')
|
|
const stat = fs.statSync(binFile)
|
|
expect(stat.mode).toBe(parseInt('100755', 8))
|
|
expect(stat.isFile()).toBe(true)
|
|
const stdout = spawnSync(binFile).stdout.toString('utf-8')
|
|
expect(stdout).toMatch('hello_world')
|
|
}
|
|
})
|
|
|
|
test("linkBins() emits global warning when bin points to path that doesn't exist", async () => {
|
|
const binTarget = tempy.directory()
|
|
const binNotExistFixture = f.prepare('bin-not-exist')
|
|
|
|
await linkBins(path.join(binNotExistFixture, 'node_modules'), binTarget, {
|
|
allowExoticManifests: true,
|
|
warn: () => {},
|
|
preferSymlinkedExecutables: true,
|
|
})
|
|
|
|
if (IS_WINDOWS) {
|
|
// cmdShim
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins([]))
|
|
} else {
|
|
// it will fix symlink file permission
|
|
expect(fs.readdirSync(binTarget)).toEqual(getExpectedBins(['meow']))
|
|
}
|
|
expect(
|
|
globalWarn
|
|
).toHaveBeenCalled()
|
|
})
|
|
})
|