Skip to content

Commit e996c6f

Browse files
committed
Fix subfolder publishing broken since npm 8.5
npm 8.5+ has a bug where `npm publish <folder>` publishes from the current working directory instead of from the specified folder. This causes the published package to incorrectly include files from both the root directory and the subfolder. Fixes #656
1 parent 3804d0b commit e996c6f

3 files changed

Lines changed: 19 additions & 43 deletions

File tree

source/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,11 @@ const np = async (input = 'patch', {packageManager, ...options}, {package_, root
196196
task(context, task) {
197197
let hasError = false;
198198

199-
return from(runPublish(getPublishCommand(options)))
199+
return from(runPublish(getPublishCommand(options), {cwd: options.rootDirectory}))
200200
.pipe(catchError(error => handleNpmError(error, task, otp => {
201201
context.otp = otp;
202202

203-
return runPublish(getPublishCommand({...options, otp}));
203+
return runPublish(getPublishCommand({...options, otp}), {cwd: options.rootDirectory});
204204
})))
205205
.pipe(
206206
// Note: Cannot use `async` here as the `await` will not finish before the error propagates.

source/npm/publish.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
1-
import path from 'node:path';
21
import {execa} from 'execa';
32

43
export const getPackagePublishArguments = options => {
54
const arguments_ = ['publish'];
65

7-
if (options.contents) {
8-
// Normalize to explicit relative path so npm doesn't interpret it as a package name
9-
const contents = path.isAbsolute(options.contents) || options.contents.startsWith('.')
10-
? options.contents
11-
: `./${options.contents}`;
12-
13-
arguments_.push(contents);
14-
}
15-
166
if (options.tag) {
177
arguments_.push('--tag', options.tag);
188
}
@@ -28,8 +18,17 @@ export const getPackagePublishArguments = options => {
2818
return arguments_;
2919
};
3020

31-
export function runPublish(arguments_) {
32-
const cp = execa(...arguments_);
21+
export function runPublish(arguments_, options = {}) {
22+
const execaOptions = {};
23+
24+
// `npm` 8.5+ has a bug where `npm publish <folder>` publishes from cwd instead of <folder>.
25+
// We work around this by changing cwd to the target directory.
26+
// https://github.com/npm/cli/issues/5136
27+
if (options.cwd) {
28+
execaOptions.cwd = options.cwd;
29+
}
30+
31+
const cp = execa(...arguments_, execaOptions);
3332

3433
cp.stdout.on('data', chunk => {
3534
// https://github.com/yarnpkg/berry/blob/a3e5695186f2aec3a68810acafc6c9b1e45191da/packages/plugin-npm/sources/npmHttpUtils.ts#L541

test/npm/publish.js

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import test from 'ava';
2-
import {getPackagePublishArguments} from '../../source/npm/publish.js';
2+
import {getPackagePublishArguments, runPublish} from '../../source/npm/publish.js';
33

44
test('no options set', t => {
55
t.deepEqual(
@@ -8,34 +8,6 @@ test('no options set', t => {
88
);
99
});
1010

11-
test('options.contents - normalizes relative path', t => {
12-
t.deepEqual(
13-
getPackagePublishArguments({contents: 'dist'}),
14-
['publish', './dist'],
15-
);
16-
});
17-
18-
test('options.contents - preserves ./ prefix', t => {
19-
t.deepEqual(
20-
getPackagePublishArguments({contents: './dist'}),
21-
['publish', './dist'],
22-
);
23-
});
24-
25-
test('options.contents - preserves ../ prefix', t => {
26-
t.deepEqual(
27-
getPackagePublishArguments({contents: '../dist'}),
28-
['publish', '../dist'],
29-
);
30-
});
31-
32-
test('options.contents - preserves absolute path', t => {
33-
t.deepEqual(
34-
getPackagePublishArguments({contents: '/absolute/path'}),
35-
['publish', '/absolute/path'],
36-
);
37-
});
38-
3911
test('options.tag', t => {
4012
t.deepEqual(
4113
getPackagePublishArguments({tag: 'beta'}),
@@ -56,3 +28,8 @@ test('options.publishScoped', t => {
5628
['publish', '--access', 'public'],
5729
);
5830
});
31+
32+
test('runPublish uses cwd option when provided', async t => {
33+
const result = await runPublish(['echo', ['test']], {cwd: '/tmp'});
34+
t.is(result.cwd, '/tmp');
35+
});

0 commit comments

Comments
 (0)