From db5e8a707846e9b9e06a968971a6a3920dd4a2cc Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Mon, 21 Apr 2025 14:30:53 -0700 Subject: [PATCH 1/2] feat: added init-private option --- docs/lib/content/commands/npm-init.md | 5 + lib/commands/init.js | 9 +- .../test/lib/commands/config.js.test.cjs | 2 + tap-snapshots/test/lib/docs.js.test.cjs | 15 ++- test/lib/commands/init.js | 122 ++++++++++++++++++ .../config/lib/definitions/definitions.js | 8 ++ .../test/type-description.js.test.cjs | 3 + 7 files changed, 162 insertions(+), 2 deletions(-) diff --git a/docs/lib/content/commands/npm-init.md b/docs/lib/content/commands/npm-init.md index 832d786e66853..4f364d01f84c0 100644 --- a/docs/lib/content/commands/npm-init.md +++ b/docs/lib/content/commands/npm-init.md @@ -87,6 +87,11 @@ Generate it without having it ask any questions: $ npm init -y ``` +Set the private flag to `true` in package.json: +```bash +$ npm init --init-private -y +``` + ### Workspaces support It's possible to create a new workspace within your project by using the diff --git a/lib/commands/init.js b/lib/commands/init.js index db33345d9427e..8d1752b537140 100644 --- a/lib/commands/init.js +++ b/lib/commands/init.js @@ -21,6 +21,7 @@ class Init extends BaseCommand { 'init-module', 'init-type', 'init-version', + 'init-private', 'yes', 'force', 'scope', @@ -133,8 +134,14 @@ class Init extends BaseCommand { const scriptShell = this.npm.config.get('script-shell') || undefined const yes = this.npm.config.get('yes') + // only send the init-private flag if it is set + const opts = { ...flatOptions } + if (this.npm.config.isDefault('init-private')) { + delete opts.initPrivate + } + await libexec({ - ...flatOptions, + ...opts, args: newArgs, localBin, globalBin, diff --git a/tap-snapshots/test/lib/commands/config.js.test.cjs b/tap-snapshots/test/lib/commands/config.js.test.cjs index 02095e17de6fb..bc0f406166a9f 100644 --- a/tap-snapshots/test/lib/commands/config.js.test.cjs +++ b/tap-snapshots/test/lib/commands/config.js.test.cjs @@ -76,6 +76,7 @@ exports[`test/lib/commands/config.js TAP config list --json > output matches sna "init-module": "{CWD}/home/.npm-init.js", "init-type": "commonjs", "init-version": "1.0.0", + "init-private": false, "init.author.email": "", "init.author.name": "", "init.author.url": "", @@ -239,6 +240,7 @@ init-author-name = "" init-author-url = "" init-license = "ISC" init-module = "{CWD}/home/.npm-init.js" +init-private = false init-type = "commonjs" init-version = "1.0.0" init.author.email = "" diff --git a/tap-snapshots/test/lib/docs.js.test.cjs b/tap-snapshots/test/lib/docs.js.test.cjs index f03284c9b9b81..da8009949f716 100644 --- a/tap-snapshots/test/lib/docs.js.test.cjs +++ b/tap-snapshots/test/lib/docs.js.test.cjs @@ -854,6 +854,15 @@ more information, or [npm init](/commands/npm-init). +#### \`init-private\` + +* Default: false +* Type: Boolean + +The value \`npm init\` should use by default for the package's private flag. + + + #### \`init-type\` * Default: "commonjs" @@ -2148,6 +2157,7 @@ Array [ "init-module", "init-type", "init-version", + "init-private", "init.author.email", "init.author.name", "init.author.url", @@ -2301,6 +2311,7 @@ Array [ "include", "include-staged", "include-workspace-root", + "init-private", "install-links", "install-strategy", "json", @@ -2456,6 +2467,7 @@ Object { "ignoreScripts": false, "includeStaged": false, "includeWorkspaceRoot": false, + "initPrivate": false, "installLinks": false, "installStrategy": "hoisted", "json": false, @@ -3245,7 +3257,7 @@ npm init <@scope> (same as \`npx <@scope>/create\`) Options: [--init-author-name ] [--init-author-url ] [--init-license ] [--init-module ] [--init-type ] [--init-version ] -[-y|--yes] [-f|--force] [--scope <@scope>] +[--init-private] [-y|--yes] [-f|--force] [--scope <@scope>] [-w|--workspace [-w|--workspace ...]] [--workspaces] [--no-workspaces-update] [--include-workspace-root] @@ -3266,6 +3278,7 @@ aliases: create, innit #### \`init-module\` #### \`init-type\` #### \`init-version\` +#### \`init-private\` #### \`yes\` #### \`force\` #### \`scope\` diff --git a/test/lib/commands/init.js b/test/lib/commands/init.js index 1e45347429258..9a73f4924e7d1 100644 --- a/test/lib/commands/init.js +++ b/test/lib/commands/init.js @@ -459,3 +459,125 @@ t.test('workspaces', async t => { t.ok(exists.isFile(), 'bin ran, creating file inside workspace') }) }) + +t.test('npm init with init-private config set', async t => { + const { npm, prefix } = await mockNpm(t, { + config: { yes: true, 'init-private': true }, + noLog: true, + }) + + await npm.exec('init', []) + + const pkg = require(resolve(prefix, 'package.json')) + t.equal(pkg.private, true, 'should set private to true when init-private is set') +}) + +t.test('npm init does not set private by default', async t => { + const { npm, prefix } = await mockNpm(t, { + config: { yes: true }, + noLog: true, + }) + + await npm.exec('init', []) + + const pkg = require(resolve(prefix, 'package.json')) + t.strictSame(pkg.private, undefined, 'should not set private by default') +}) + +t.test('user‑set init-private IS forwarded', async t => { + const { npm, prefix } = await mockNpm(t, { + config: { yes: true, 'init-private': true }, + noLog: true, + }) + + await npm.exec('init', []) + + const pkg = require(resolve(prefix, 'package.json')) + t.strictSame(pkg.private, true, 'should set private to true when init-private is set') +}) + +t.test('user‑set init-private IS forwarded when false', async t => { + const { npm, prefix } = await mockNpm(t, { + config: { yes: true, 'init-private': false }, + noLog: true, + }) + + await npm.exec('init', []) + + const pkg = require(resolve(prefix, 'package.json')) + t.strictSame(pkg.private, false, 'should set private to false when init-private is false') +}) + +t.test('No init-private is respected in workspaces', async t => { + const { npm, prefix } = await mockNpm(t, { + prefixDir: { + 'package.json': JSON.stringify({ + name: 'top-level', + }), + }, + config: { workspace: 'a', yes: true }, + noLog: true, + }) + + await npm.exec('init', []) + + const pkg = require(resolve(prefix, 'a/package.json')) + t.strictSame(pkg.private, undefined, 'workspace package.json has no private field set') +}) + +t.test('init-private is respected in workspaces', async t => { + const { npm, prefix } = await mockNpm(t, { + prefixDir: { + 'package.json': JSON.stringify({ + name: 'top-level', + }), + }, + config: { workspace: 'a', yes: true, 'init-private': true }, + noLog: true, + }) + + await npm.exec('init', []) + + const pkg = require(resolve(prefix, 'a/package.json')) + t.equal(pkg.private, true, 'workspace package.json has private field set') +}) + +t.test('create‑initializer path: init-private flag is forwarded via args', async t => { + const calls = [] + const libexecStub = async opts => calls.push(opts) + + const { npm } = await mockNpm(t, { + libnpmexec: libexecStub, + // user set the flag in their config + config: { yes: true, 'init-private': true }, + noLog: true, + }) + + await npm.exec('init', ['create-bar']) + + t.ok(calls[0].initPrivate, 'init-private included in options') + + // Also verify the test for when isDefault returns true + calls.length = 0 + npm.config.isDefault = () => true + + await npm.exec('init', ['create-bar']) + + t.equal(calls[0].initPrivate, undefined, 'init-private not included when using default') +}) + +t.test('create‑initializer path: false init-private is forwarded', async t => { + const calls = [] + const libexecStub = async opts => calls.push(opts) + + const { npm } = await mockNpm(t, { + libnpmexec: libexecStub, + // explicitly set to false + config: { yes: true, 'init-private': false }, + noLog: true, + }) + + await npm.exec('init', ['create-baz']) + + t.equal(calls[0].initPrivate, false, 'false init-private value is properly forwarded') +}) diff --git a/workspaces/config/lib/definitions/definitions.js b/workspaces/config/lib/definitions/definitions.js index 6099fc5fbf128..d703840e0e928 100644 --- a/workspaces/config/lib/definitions/definitions.js +++ b/workspaces/config/lib/definitions/definitions.js @@ -971,6 +971,14 @@ const definitions = { version number, if not already set in package.json. `, }), + 'init-private': new Definition('init-private', { + default: false, + type: Boolean, + description: ` + The value \`npm init\` should use by default for the package's private flag. + `, + flatten, + }), // these "aliases" are historically supported in .npmrc files, unfortunately // They should be removed in a future npm version. 'init.author.email': new Definition('init.author.email', { diff --git a/workspaces/config/tap-snapshots/test/type-description.js.test.cjs b/workspaces/config/tap-snapshots/test/type-description.js.test.cjs index 618b6752116eb..270916887c650 100644 --- a/workspaces/config/tap-snapshots/test/type-description.js.test.cjs +++ b/workspaces/config/tap-snapshots/test/type-description.js.test.cjs @@ -231,6 +231,9 @@ Object { "init-module": Array [ "valid filesystem path", ], + "init-private": Array [ + "boolean value (true or false)", + ], "init-type": Array [ Function String(), ], From a4a3b5ca44d97b1eac926e6b8fb98315cc5ce2d5 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Wed, 23 Apr 2025 09:32:46 -0700 Subject: [PATCH 2/2] deps: init-package-json@8.2.1 --- .../init-package-json/lib/default-input.js | 22 ++++++++++++++++++- node_modules/init-package-json/package.json | 4 ++-- package-lock.json | 8 +++---- package.json | 2 +- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/node_modules/init-package-json/lib/default-input.js b/node_modules/init-package-json/lib/default-input.js index 3b38c77606e32..d72feee7f44d3 100644 --- a/node_modules/init-package-json/lib/default-input.js +++ b/node_modules/init-package-json/lib/default-input.js @@ -8,7 +8,17 @@ const npa = require('npm-package-arg') const semver = require('semver') // more popular packages should go here, maybe? -const isTestPkg = (p) => !!p.match(/^(expresso|mocha|tap|coffee-script|coco|streamline)$/) +const testPkgs = [ + 'coco', + 'coffee-script', + 'expresso', + 'jasmine', + 'jest', + 'mocha', + 'streamline', + 'tap', +] +const isTestPkg = p => testPkgs.includes(p) const invalid = (msg) => Object.assign(new Error(msg), { notValid: true }) @@ -266,3 +276,13 @@ const type = package.type || getConfig('type') || 'commonjs' exports.type = yes ? type : prompt('type', type, (data) => { return data }) + +// Only include private field if it already exists or if explicitly set in config +const configPrivate = getConfig('private') +if (package.private !== undefined || configPrivate !== undefined) { + if (package.private !== undefined) { + exports.private = package.private + } else if (!config.isDefault || !config.isDefault('init-private')) { + exports.private = configPrivate + } +} diff --git a/node_modules/init-package-json/package.json b/node_modules/init-package-json/package.json index c264eb44f9749..722e74fc16cb0 100644 --- a/node_modules/init-package-json/package.json +++ b/node_modules/init-package-json/package.json @@ -1,6 +1,6 @@ { "name": "init-package-json", - "version": "8.0.0", + "version": "8.2.1", "main": "lib/init-package-json.js", "scripts": { "test": "tap", @@ -29,7 +29,7 @@ "validate-npm-package-name": "^6.0.0" }, "devDependencies": { - "@npmcli/config": "^9.0.0", + "@npmcli/config": "^10.0.0", "@npmcli/eslint-config": "^5.0.0", "@npmcli/template-oss": "4.23.4", "tap": "^16.0.1" diff --git a/package-lock.json b/package-lock.json index 4f03197628b70..0510188f4854c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -106,7 +106,7 @@ "graceful-fs": "^4.2.11", "hosted-git-info": "^8.1.0", "ini": "^5.0.0", - "init-package-json": "^8.0.0", + "init-package-json": "^8.2.1", "is-cidr": "^5.1.1", "json-parse-even-better-errors": "^4.0.0", "libnpmaccess": "^10.0.0", @@ -9493,9 +9493,9 @@ } }, "node_modules/init-package-json": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-8.0.0.tgz", - "integrity": "sha512-zKgxfaGt6Zzi8VBSInOK0CYDigA9gzDCWPnSzGIoUlTU/5w7qIyi+6MyJYX96mMlxDGrIR85FhQszVyodYfB9g==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-8.2.1.tgz", + "integrity": "sha512-8lhupwQjiwCJzwVILceTq0Kvyj+0cFun0jvmMz0TwCFFgCAqLV6tZl07VAexh8YFOWwIN9jxN+XHkW27fy1nZg==", "inBundle": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index e365c42c0c28c..fe258f8bdbbdd 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "graceful-fs": "^4.2.11", "hosted-git-info": "^8.1.0", "ini": "^5.0.0", - "init-package-json": "^8.0.0", + "init-package-json": "^8.2.1", "is-cidr": "^5.1.1", "json-parse-even-better-errors": "^4.0.0", "libnpmaccess": "^10.0.0",