From ec520ce32d5e834a32ebd58491df4200e01ce690 Mon Sep 17 00:00:00 2001 From: nlf Date: Tue, 30 Mar 2021 08:05:41 -0700 Subject: [PATCH] feat(set-script): implement workspaces Implements workspaces for set-script, also refactors tests to mock as little as possible. PR-URL: https://github.com/npm/cli/pull/2998 Credit: @nlf Close: #2998 Reviewed-by: @wraithgar --- lib/set-script.js | 81 ++++-- .../test-lib-utils-npm-usage.js-TAP.test.js | 3 + test/lib/set-script.js | 236 +++++++++--------- 3 files changed, 187 insertions(+), 133 deletions(-) diff --git a/lib/set-script.js b/lib/set-script.js index 9d4aadad558f..b31e123becd8 100644 --- a/lib/set-script.js +++ b/lib/set-script.js @@ -3,6 +3,7 @@ const fs = require('fs') const parseJSON = require('json-parse-even-better-errors') const rpj = require('read-package-json-fast') const { resolve } = require('path') +const getWorkspaces = require('./workspaces/get-workspaces.js') const BaseCommand = require('./base-command.js') class SetScript extends BaseCommand { @@ -11,6 +12,11 @@ class SetScript extends BaseCommand { return 'Set tasks in the scripts section of package.json' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return ['workspace', 'workspaces'] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'set-script' @@ -31,49 +37,90 @@ class SetScript extends BaseCommand { } } - exec (args, cb) { - this.set(args).then(() => cb()).catch(cb) - } - - async set (args) { + validate (args) { if (process.env.npm_lifecycle_event === 'postinstall') throw new Error('Scripts can’t set from the postinstall script') // Parse arguments if (args.length !== 2) throw new Error(`Expected 2 arguments: got ${args.length}`) + } + exec (args, cb) { + this.set(args).then(() => cb()).catch(cb) + } + + async set (args) { + this.validate(args) + const warn = this.setScript(this.npm.localPrefix, args[0], args[1]) + if (warn) + log.warn('set-script', `Script "${args[0]}" was overwritten`) + } + + execWorkspaces (args, filters, cb) { + this.setWorkspaces(args, filters).then(() => cb()).catch(cb) + } + + async setWorkspaces (args, filters) { + this.validate(args) + const workspaces = + await getWorkspaces(filters, { path: this.npm.localPrefix }) + + for (const [name, path] of workspaces) { + try { + const warn = this.setScript(path, args[0], args[1]) + if (warn) { + log.warn('set-script', `Script "${args[0]}" was overwritten`) + log.warn(` in workspace: ${name}`) + log.warn(` at location: ${path}`) + } + } catch (err) { + log.error('set-script', err.message) + log.error(` in workspace: ${name}`) + log.error(` at location: ${path}`) + process.exitCode = 1 + } + } + } + + // returns a Boolean that will be true if + // the requested script was overwritten + // and false if it was set as a new script + setScript (path, name, value) { // Set the script let manifest let warn = false + try { - manifest = fs.readFileSync(this.npm.localPrefix + '/package.json', 'utf-8') + manifest = fs.readFileSync(resolve(path, 'package.json'), 'utf-8') } catch (error) { throw new Error('package.json not found') } + try { manifest = parseJSON(manifest) } catch (error) { throw new Error(`Invalid package.json: ${error}`) } + if (!manifest.scripts) manifest.scripts = {} - if (manifest.scripts[args[0]] && manifest.scripts[args[0]] !== args[1]) + + if (manifest.scripts[name] && manifest.scripts[name] !== value) warn = true - manifest.scripts[args[0]] = args[1] + manifest.scripts[name] = value + // format content - const packageJsonInfo = await rpj(this.npm.localPrefix + '/package.json') const { [Symbol.for('indent')]: indent, [Symbol.for('newline')]: newline, - } = packageJsonInfo - const format = indent === undefined ? ' ' : indent - const eol = newline === undefined ? '\n' : newline - const content = (JSON.stringify(manifest, null, format) + '\n') - .replace(/\n/g, eol) - fs.writeFileSync(this.npm.localPrefix + '/package.json', content) - if (warn) - log.warn('set-script', `Script "${args[0]}" was overwritten`) + } = manifest + + const content = (JSON.stringify(manifest, null, indent) + '\n') + .replace(/\n/g, newline) + fs.writeFileSync(resolve(path, 'package.json'), content) + + return warn } } module.exports = SetScript diff --git a/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js b/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js index 2d229a2691f8..4f9c1b44ebf3 100644 --- a/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js +++ b/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js @@ -771,6 +771,9 @@ All commands: Usage: npm set-script [