Skip to content

Commit c9be2d1

Browse files
reggiCopilot
andauthored
feat: publish --access=private alias for restricted (#9153)
## Summary Adds `private` as a valid value for `--access` in `npm publish`, as an alias for `restricted`. ## Motivation The `--access` flag on `npm publish` currently accepts `restricted` and `public`. However, in everyday usage, the npm community colloquially refers to non-public packages as "private packages" — not "restricted packages." The npm website, the npm registry, and `npm access` all use the term "private" to describe these packages. ### `npm publish --access` vs `npm access` These are two different commands that deal with package visibility in different ways: - **`npm publish --access=<public|restricted>`** sets the visibility of a package **at publish time**. This is only relevant for scoped packages, where the default for new packages is `public`. - **`npm access set status=<public|private>`** changes the visibility of an **already published** package. Notably, `npm access` already uses `private` (not `restricted`) as its term for non-public packages. This inconsistency means that a user who runs `npm access set status=private` might naturally try `npm publish --access=private` and get an error. Since everyone already calls them "private packages," the CLI should accept that term too. This PR resolves that by accepting `private` as a synonym for `restricted` during publish. ## Changes - Added `private` to the valid values for the `access` config definition - The `flatten` function maps `private` → `restricted` so the registry always receives `restricted` - Updated documentation to note that `private` is an alias for `restricted` - Added tests for the `--access=private` flow --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7068d42 commit c9be2d1

7 files changed

Lines changed: 74 additions & 6 deletions

File tree

tap-snapshots/test/lib/commands/publish.js.test.cjs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,28 @@ exports[`test/lib/commands/publish.js TAP prioritize CLI flags over publishConfi
173173
+ @npmcli/test-package@1.0.0
174174
`
175175

176+
exports[`test/lib/commands/publish.js TAP private access > must match snapshot 1`] = `
177+
Array [
178+
"package: @npm/test-package@1.0.0",
179+
"Tarball Contents",
180+
"55B package.json",
181+
"Tarball Details",
182+
"name: @npm/test-package",
183+
"version: 1.0.0",
184+
"filename: npm-test-package-1.0.0.tgz",
185+
"package size: {size}",
186+
"unpacked size: 55 B",
187+
"shasum: {sha}",
188+
"integrity: {integrity}
189+
"total files: 1",
190+
"Publishing to https://registry.npmjs.org/ with tag latest and restricted access",
191+
]
192+
`
193+
194+
exports[`test/lib/commands/publish.js TAP private access > new package version 1`] = `
195+
+ @npm/test-package@1.0.0
196+
`
197+
176198
exports[`test/lib/commands/publish.js TAP public access > must match snapshot 1`] = `
177199
Array [
178200
"package: @npm/test-package@1.0.0",

tap-snapshots/test/lib/docs.js.test.cjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ safer to use a registry-provided authentication bearer token stored in the
189189
190190
* Default: 'public' for new packages, existing packages it will not change the
191191
current level
192-
* Type: null, "restricted", or "public"
192+
* Type: null, "restricted", "public", or "private"
193193
194194
If you do not want your scoped package to be publicly viewable (and
195195
installable) set \`--access=restricted\`.
@@ -201,6 +201,8 @@ packages. Specifying a value of \`restricted\` or \`public\` during publish will
201201
change the access for an existing package the same way that \`npm access set
202202
status\` would.
203203
204+
The value \`private\` is an alias for \`restricted\`.
205+
204206
205207
206208
#### \`all\`
@@ -5416,7 +5418,7 @@ Usage:
54165418
npm publish <package-spec>
54175419
54185420
Options:
5419-
[--tag <tag>] [--access <restricted|public>] [--dry-run] [--otp <otp>]
5421+
[--tag <tag>] [--access <restricted|public|private>] [--dry-run] [--otp <otp>]
54205422
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
54215423
[--workspaces] [--include-workspace-root] [--provenance|--provenance-file <file>]
54225424

test/lib/commands/publish.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,27 @@ t.test('restricted access', async t => {
742742
t.matchSnapshot(logs.notice)
743743
})
744744

745+
t.test('private access', async t => {
746+
const packageJson = {
747+
name: '@npm/test-package',
748+
version: '1.0.0',
749+
}
750+
const { npm, joinedOutput, logs, registry } = await loadNpmWithRegistry(t, {
751+
config: {
752+
...auth,
753+
access: 'private',
754+
},
755+
prefixDir: {
756+
'package.json': JSON.stringify(packageJson, null, 2),
757+
},
758+
authorization: token,
759+
})
760+
registry.publish('@npm/test-package', { packageJson, access: 'restricted' })
761+
await npm.exec('publish', [])
762+
t.matchSnapshot(joinedOutput(), 'new package version')
763+
t.matchSnapshot(logs.notice)
764+
})
765+
745766
t.test('public access', async t => {
746767
const { npm, joinedOutput, logs, registry } = await loadNpmWithRegistry(t, {
747768
config: {

workspaces/config/lib/definitions/definitions.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ const definitions = {
154154
defaultDescription: `
155155
'public' for new packages, existing packages it will not change the current level
156156
`,
157-
type: [null, 'restricted', 'public'],
157+
type: [null, 'restricted', 'public', 'private'],
158158
description: `
159159
If you do not want your scoped package to be publicly viewable (and
160160
installable) set \`--access=restricted\`.
@@ -165,8 +165,13 @@ const definitions = {
165165
packages. Specifying a value of \`restricted\` or \`public\` during
166166
publish will change the access for an existing package the same way that
167167
\`npm access set status\` would.
168+
169+
The value \`private\` is an alias for \`restricted\`.
168170
`,
169-
flatten,
171+
flatten (key, obj, flatOptions) {
172+
const value = obj[key]
173+
flatOptions.access = value === 'private' ? 'restricted' : value
174+
},
170175
}),
171176
all: new Definition('all', {
172177
default: false,

workspaces/config/tap-snapshots/test/type-description.js.test.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Object {
1515
null,
1616
"restricted",
1717
"public",
18+
"private",
1819
],
1920
"all": Array [
2021
"boolean value (true or false)",

workspaces/config/test/definitions/definitions.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@ t.test('basic flattening function camelCases from css-case', t => {
3333
t.end()
3434
})
3535

36+
t.test('access flattening maps private to restricted', t => {
37+
const definitions = mockDefs()
38+
const flatPrivate = {}
39+
definitions.access.flatten('access', { access: 'private' }, flatPrivate)
40+
t.equal(flatPrivate.access, 'restricted', 'private is mapped to restricted')
41+
const flatRestricted = {}
42+
definitions.access.flatten('access', { access: 'restricted' }, flatRestricted)
43+
t.equal(flatRestricted.access, 'restricted', 'restricted is passed through')
44+
const flatPublic = {}
45+
definitions.access.flatten('access', { access: 'public' }, flatPublic)
46+
t.equal(flatPublic.access, 'public', 'public is passed through')
47+
const flatNull = {}
48+
definitions.access.flatten('access', { access: null }, flatNull)
49+
t.equal(flatNull.access, null, 'null is passed through')
50+
t.end()
51+
})
52+
3653
t.test('editor', t => {
3754
t.test('has EDITOR and VISUAL, use EDITOR', t => {
3855
mockGlobals(t, { 'process.env': { EDITOR: 'vim', VISUAL: 'mate' } })

workspaces/config/test/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ loglevel = yolo
412412
['warn', 'invalid config', 'omit="cucumber"', 'set in command line options'],
413413
['warn', 'invalid config', 'Must be one or more of:', 'dev, optional, peer'],
414414
['warn', 'invalid config', 'access="blueberry"', 'set in command line options'],
415-
['warn', 'invalid config', 'Must be one of:', 'null, restricted, public'],
415+
['warn', 'invalid config', 'Must be one of:', 'null, restricted, public, private'],
416416
['warn', 'invalid config', 'multiple-numbers="what kind of fruit is not a number"',
417417
'set in command line options'],
418418
['warn', 'invalid config', 'Must be one or more', 'numeric value'],
@@ -608,7 +608,7 @@ loglevel = yolo
608608
['warn', 'invalid config', 'omit="cucumber"', 'set in command line options'],
609609
['warn', 'invalid config', 'Must be one or more of:', 'dev, optional, peer'],
610610
['warn', 'invalid config', 'access="blueberry"', 'set in command line options'],
611-
['warn', 'invalid config', 'Must be one of:', 'null, restricted, public'],
611+
['warn', 'invalid config', 'Must be one of:', 'null, restricted, public, private'],
612612
['warn', 'invalid config', 'multiple-numbers="what kind of fruit is not a number"',
613613
'set in command line options'],
614614
['warn', 'invalid config', 'Must be one or more', 'numeric value'],

0 commit comments

Comments
 (0)