fix npx for non-interactive shells · npm/cli@e859fba (original) (raw)

`@@ -19,6 +19,7 @@ class Arborist {

`

19

19

`}

`

20

20

``

21

21

`let PROGRESS_ENABLED = true

`

``

22

`+

const LOG_WARN = []

`

22

23

`const npm = {

`

23

24

`flatOptions: {

`

24

25

`yes: true,

`

`@@ -41,6 +42,9 @@ const npm = {

`

41

42

`},

`

42

43

`enableProgress: () => {

`

43

44

`PROGRESS_ENABLED = true

`

``

45

`+

},

`

``

46

`+

warn: (...args) => {

`

``

47

`+

LOG_WARN.push(args)

`

44

48

`}

`

45

49

`}

`

46

50

`}

`

`@@ -88,6 +92,7 @@ t.afterEach(cb => {

`

88

92

`READ.length = 0

`

89

93

`READ_RESULT = ''

`

90

94

`READ_ERROR = null

`

``

95

`+

LOG_WARN.length = 0

`

91

96

`npm.flatOptions.legacyPeerDeps = false

`

92

97

`npm.flatOptions.package = []

`

93

98

`npm.flatOptions.call = ''

`

`@@ -464,7 +469,16 @@ t.test('positional args and --call together is an error', t => {

`

464

469

`return exec(['foo'], er => t.equal(er, exec.usage))

`

465

470

`})

`

466

471

``

467

``

`-

t.test('prompt when installs are needed if not already present', async t => {

`

``

472

`+

t.test('prompt when installs are needed if not already present and shell is a TTY', async t => {

`

``

473

`+

const stdoutTTY = process.stdout.isTTY

`

``

474

`+

const stdinTTY = process.stdin.isTTY

`

``

475

`+

t.teardown(() => {

`

``

476

`+

process.stdout.isTTY = stdoutTTY

`

``

477

`+

process.stdin.isTTY = stdinTTY

`

``

478

`+

})

`

``

479

`+

process.stdout.isTTY = true

`

``

480

`+

process.stdin.isTTY = true

`

``

481

+

468

482

`const packages = ['foo', 'bar']

`

469

483

`READ_RESULT = 'yolo'

`

470

484

``

`@@ -522,7 +536,138 @@ t.test('prompt when installs are needed if not already present', async t => {

`

522

536

`}])

`

523

537

`})

`

524

538

``

``

539

`+

t.test('skip prompt when installs are needed if not already present and shell is not a tty (multiple packages)', async t => {

`

``

540

`+

const stdoutTTY = process.stdout.isTTY

`

``

541

`+

const stdinTTY = process.stdin.isTTY

`

``

542

`+

t.teardown(() => {

`

``

543

`+

process.stdout.isTTY = stdoutTTY

`

``

544

`+

process.stdin.isTTY = stdinTTY

`

``

545

`+

})

`

``

546

`+

process.stdout.isTTY = false

`

``

547

`+

process.stdin.isTTY = false

`

``

548

+

``

549

`+

const packages = ['foo', 'bar']

`

``

550

`+

READ_RESULT = 'yolo'

`

``

551

+

``

552

`+

npm.flatOptions.package = packages

`

``

553

`+

npm.flatOptions.yes = undefined

`

``

554

+

``

555

`` +

const add = packages.map(p => ${p}@).sort((a, b) => a.localeCompare(b))

``

``

556

`+

const path = t.testdir()

`

``

557

`+

const installDir = resolve('cache-dir/_npx/07de77790e5f40f2')

`

``

558

`+

npm.localPrefix = path

`

``

559

`+

ARB_ACTUAL_TREE[path] = {

`

``

560

`+

children: new Map()

`

``

561

`+

}

`

``

562

`+

ARB_ACTUAL_TREE[installDir] = {

`

``

563

`+

children: new Map()

`

``

564

`+

}

`

``

565

`+

MANIFESTS.foo = {

`

``

566

`+

name: 'foo',

`

``

567

`+

version: '1.2.3',

`

``

568

`+

bin: {

`

``

569

`+

foo: 'foo'

`

``

570

`+

},

`

``

571

`+

_from: 'foo@'

`

``

572

`+

}

`

``

573

`+

MANIFESTS.bar = {

`

``

574

`+

name: 'bar',

`

``

575

`+

version: '1.2.3',

`

``

576

`+

bin: {

`

``

577

`+

bar: 'bar'

`

``

578

`+

},

`

``

579

`+

_from: 'bar@'

`

``

580

`+

}

`

``

581

`+

await exec(['foobar'], er => {

`

``

582

`+

if (er) {

`

``

583

`+

throw er

`

``

584

`+

}

`

``

585

`+

})

`

``

586

`+

t.strictSame(MKDIRPS, [installDir], 'need to make install dir')

`

``

587

`+

t.match(ARB_CTOR, [ { package: packages, path } ])

`

``

588

`+

t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages')

`

``

589

`+

t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')

`

``

590

`` +

const PATH = ${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}

``

``

591

`+

t.match(RUN_SCRIPTS, [{

`

``

592

`+

pkg: { scripts: { npx: 'foobar' } },

`

``

593

`+

banner: false,

`

``

594

`+

path: process.cwd(),

`

``

595

`+

stdioString: true,

`

``

596

`+

event: 'npx',

`

``

597

`+

env: { PATH },

`

``

598

`+

stdio: 'inherit'

`

``

599

`+

}])

`

``

600

`+

t.strictSame(READ, [], 'should not have prompted')

`

``

601

`+

t.strictSame(LOG_WARN, [['exec', 'The following packages were not found and will be installed: bar, foo']], 'should have printed a warning')

`

``

602

`+

})

`

``

603

+

``

604

`+

t.test('skip prompt when installs are needed if not already present and shell is not a tty (single package)', async t => {

`

``

605

`+

const stdoutTTY = process.stdout.isTTY

`

``

606

`+

const stdinTTY = process.stdin.isTTY

`

``

607

`+

t.teardown(() => {

`

``

608

`+

process.stdout.isTTY = stdoutTTY

`

``

609

`+

process.stdin.isTTY = stdinTTY

`

``

610

`+

})

`

``

611

`+

process.stdout.isTTY = false

`

``

612

`+

process.stdin.isTTY = false

`

``

613

+

``

614

`+

const packages = ['foo']

`

``

615

`+

READ_RESULT = 'yolo'

`

``

616

+

``

617

`+

npm.flatOptions.package = packages

`

``

618

`+

npm.flatOptions.yes = undefined

`

``

619

+

``

620

`` +

const add = packages.map(p => ${p}@).sort((a, b) => a.localeCompare(b))

``

``

621

`+

const path = t.testdir()

`

``

622

`+

const installDir = resolve('cache-dir/_npx/f7fbba6e0636f890')

`

``

623

`+

npm.localPrefix = path

`

``

624

`+

ARB_ACTUAL_TREE[path] = {

`

``

625

`+

children: new Map()

`

``

626

`+

}

`

``

627

`+

ARB_ACTUAL_TREE[installDir] = {

`

``

628

`+

children: new Map()

`

``

629

`+

}

`

``

630

`+

MANIFESTS.foo = {

`

``

631

`+

name: 'foo',

`

``

632

`+

version: '1.2.3',

`

``

633

`+

bin: {

`

``

634

`+

foo: 'foo'

`

``

635

`+

},

`

``

636

`+

_from: 'foo@'

`

``

637

`+

}

`

``

638

`+

await exec(['foobar'], er => {

`

``

639

`+

if (er) {

`

``

640

`+

throw er

`

``

641

`+

}

`

``

642

`+

})

`

``

643

`+

t.strictSame(MKDIRPS, [installDir], 'need to make install dir')

`

``

644

`+

t.match(ARB_CTOR, [ { package: packages, path } ])

`

``

645

`+

t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install the package')

`

``

646

`+

t.equal(PROGRESS_ENABLED, true, 'progress re-enabled')

`

``

647

`` +

const PATH = ${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}

``

``

648

`+

t.match(RUN_SCRIPTS, [{

`

``

649

`+

pkg: { scripts: { npx: 'foobar' } },

`

``

650

`+

banner: false,

`

``

651

`+

path: process.cwd(),

`

``

652

`+

stdioString: true,

`

``

653

`+

event: 'npx',

`

``

654

`+

env: { PATH },

`

``

655

`+

stdio: 'inherit'

`

``

656

`+

}])

`

``

657

`+

t.strictSame(READ, [], 'should not have prompted')

`

``

658

`+

t.strictSame(LOG_WARN, [['exec', 'The following package was not found and will be installed: foo']], 'should have printed a warning')

`

``

659

`+

})

`

``

660

+

525

661

`t.test('abort if prompt rejected', async t => {

`

``

662

`+

const stdoutTTY = process.stdout.isTTY

`

``

663

`+

const stdinTTY = process.stdin.isTTY

`

``

664

`+

t.teardown(() => {

`

``

665

`+

process.stdout.isTTY = stdoutTTY

`

``

666

`+

process.stdin.isTTY = stdinTTY

`

``

667

`+

})

`

``

668

`+

process.stdout.isTTY = true

`

``

669

`+

process.stdin.isTTY = true

`

``

670

+

526

671

`const packages = ['foo', 'bar']

`

527

672

`READ_RESULT = 'no, why would I want such a thing??'

`

528

673

``

`@@ -570,6 +715,15 @@ t.test('abort if prompt rejected', async t => {

`

570

715

`})

`

571

716

``

572

717

`t.test('abort if prompt false', async t => {

`

``

718

`+

const stdoutTTY = process.stdout.isTTY

`

``

719

`+

const stdinTTY = process.stdin.isTTY

`

``

720

`+

t.teardown(() => {

`

``

721

`+

process.stdout.isTTY = stdoutTTY

`

``

722

`+

process.stdin.isTTY = stdinTTY

`

``

723

`+

})

`

``

724

`+

process.stdout.isTTY = true

`

``

725

`+

process.stdin.isTTY = true

`

``

726

+

573

727

`const packages = ['foo', 'bar']

`

574

728

`READ_ERROR = 'canceled'

`

575

729

``

`@@ -617,6 +771,15 @@ t.test('abort if prompt false', async t => {

`

617

771

`})

`

618

772

``

619

773

`t.test('abort if -n provided', async t => {

`

``

774

`+

const stdoutTTY = process.stdout.isTTY

`

``

775

`+

const stdinTTY = process.stdin.isTTY

`

``

776

`+

t.teardown(() => {

`

``

777

`+

process.stdout.isTTY = stdoutTTY

`

``

778

`+

process.stdin.isTTY = stdinTTY

`

``

779

`+

})

`

``

780

`+

process.stdout.isTTY = true

`

``

781

`+

process.stdin.isTTY = true

`

``

782

+

620

783

`const packages = ['foo', 'bar']

`

621

784

``

622

785

`npm.flatOptions.package = packages

`