Socket
Socket
Sign inDemoInstall

lerna-update-wizard

Package Overview
Dependencies
Maintainers
1
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

lerna-update-wizard - npm Package Compare versions

Comparing version 0.10.0 to 0.11.0

src/utils/invariant.js

2

package.json

@@ -6,3 +6,3 @@ {

},
"version": "0.10.0",
"version": "0.11.0",
"main": "index.js",

@@ -9,0 +9,0 @@ "license": "MIT",

@@ -73,2 +73,24 @@ # Lerna Update Wizard

### Non-interactive mode
The script can run without prompting you for input. Simply specify the `--non-interactive` flag:
```bash
$ lernaupdate --non-interactive --dependency lodash@4.2.1 ./my-project
```
The script will tell you if you need to specify any additional input flags based on the state of your mono repo.
For instance, you might need/wish to include information about which packages to affect and which type of installation to perform if the dependency is a first-time install:
```bash
$ lernaupdate --non-interactive \
--dependency lodash@4.2.1 \
--packages packages/utils,packages/tools \
--new-installs-mode dev \
./my-project
```
**Note: Git features are not available for `--non-interactive` mode.**
### Yarn support

@@ -75,0 +97,0 @@

@@ -14,2 +14,4 @@ const path = require("path");

const plural = require("./utils/plural");
const invariant = require("./utils/invariant");
const parseDependency = require("./utils/parseDependency");
const sanitizeGitBranchName = require("./utils/sanitizeGitBranchName");

@@ -28,7 +30,15 @@

// Validate flags
flags.nonInteractive &&
invariant(
flags.dependency,
"`--dependency` option must be specified in non-interactive mode"
);
const projectPackageJsonPath = resolve(projectDir, "package.json");
if (!(await fileExists(projectPackageJsonPath))) {
throw new Error("No 'package.json' found in specified directory");
}
invariant(
await fileExists(projectPackageJsonPath),
"No 'package.json' found in specified directory"
);

@@ -51,6 +61,8 @@ const { name: projectName } = require(projectPackageJsonPath);

const defaultPackagesGlobs = flags.packages
? flags.packages.split(",")
: lernaConfig.packages || ["packages/*"];
const packagesRead = await globby(
(lernaConfig.packages || ["packages/*"]).map(glob =>
resolve(projectDir, glob, "package.json")
),
defaultPackagesGlobs.map(glob => resolve(projectDir, glob, "package.json")),
{ expandDirectories: true }

@@ -67,5 +79,3 @@ );

if (packages.length === 0) {
throw new Error("No packages found. Is this a Lerna project?");
}
invariant(packages.length > 0, "No packages found. Is this a Lerna project?");

@@ -147,50 +157,57 @@ ui.logBottom("");

const { targetDependency } = await inquirer.prompt([
{
type: "autocomplete",
name: "targetDependency",
message: "Select a dependency to upgrade:",
pageSize: 15,
source: (_ignore_, input) => {
const itemize = value => ({
value,
name: `${chalk.white(value)} ${chalk[dependencyMap[value].color](
`(${plural(
"version",
"versions",
dependencyMap[value].versions.length
)})`
)}`,
});
let targetDependency =
flags.dependency && parseDependency(flags.dependency).name;
const sorter = flags.dedupe
? (a, b) =>
dependencyMap[b].versions.length -
dependencyMap[a].versions.length
: undefined;
if (!targetDependency) {
const { targetDependency: promptedTarget } = await inquirer.prompt([
{
type: "autocomplete",
name: "targetDependency",
message: "Select a dependency to upgrade:",
pageSize: 15,
source: (_ignore_, input) => {
const itemize = value => ({
value,
name: `${chalk.white(value)} ${chalk[dependencyMap[value].color](
`(${plural(
"version",
"versions",
dependencyMap[value].versions.length
)})`
)}`,
});
let results = input
? allDependencies
.filter(name => new RegExp(input).test(name))
.sort(sorter)
.map(itemize)
: allDependencies.sort(sorter).map(itemize);
const sorter = flags.dedupe
? (a, b) =>
dependencyMap[b].versions.length -
dependencyMap[a].versions.length
: undefined;
if (input && !allDependencies.includes(input)) {
results = [
...results,
{
name: `${input} ${chalk.green.bold("[+ADD]")}`,
value: input,
},
];
}
let results = input
? allDependencies
.filter(name => new RegExp(input).test(name))
.sort(sorter)
.map(itemize)
: allDependencies.sort(sorter).map(itemize);
return Promise.resolve(results);
if (input && !allDependencies.includes(input)) {
results = [
...results,
{
name: `${input} ${chalk.green.bold("[+ADD]")}`,
value: input,
},
];
}
return Promise.resolve(results);
},
},
},
]);
]);
const isNewDependency = !allDependencies.includes(targetDependency);
targetDependency = promptedTarget;
}
// Look up NPM dependency and its versions
const npmPackageInfoRaw = await runCommand(

@@ -210,65 +227,113 @@ `npm info ${targetDependency} versions dist-tags --json`,

const { targetPackages } = await inquirer.prompt([
{
type: "checkbox",
name: "targetPackages",
message: "Select packages to affect:",
pageSize: 15,
choices: packages.map(({ config: { name: packageName } }) => {
if (isNewDependency) {
// Target packages selection
const isNewDependency = !allDependencies.includes(targetDependency);
if (flags.nonInteractive && isNewDependency) {
invariant(
flags.newInstallsMode,
`"${targetDependency}" is a first-time install for one or more packages.`,
"In non-interactive mode you must specify the --new-installs-mode flag (prod|dev|peer) in this situation."
);
}
let targetPackages;
if (flags.nonInteractive && !flags.packages) {
const installedPackages = packages
.filter(
({ config: { name } }) =>
!!(
dependencyMap[targetDependency] &&
dependencyMap[targetDependency].packs[name]
)
)
.map(({ config: { name } }) => name);
invariant(
installedPackages.length > 0,
`No packages contain the dependency "${targetDependency}".`,
"In non-interactive mode you must specify the --packages flag in this situation,",
"so the script can know which packages install it in."
);
targetPackages = installedPackages;
} else if (flags.packages) {
targetPackages = packages.map(({ config: { name } }) => name);
}
if (!targetPackages) {
const { targetPackages: promptedTarget } = await inquirer.prompt([
{
type: "checkbox",
name: "targetPackages",
message: "Select packages to affect:",
pageSize: 15,
choices: packages.map(({ config: { name: packageName } }) => {
if (isNewDependency) {
return {
name: packageName,
value: packageName,
checked: false,
};
}
const { version, source } =
dependencyMap[targetDependency].packs[packageName] || {};
const versionBit = version ? ` (${version})` : "";
const sourceBit =
source === "devDependencies" ? chalk.white(" (dev)") : "";
return {
name: packageName,
name: `${packageName}${versionBit}${sourceBit}`,
value: packageName,
checked: false,
checked: !!version,
};
}
}),
},
]);
const { version, source } =
dependencyMap[targetDependency].packs[packageName] || {};
targetPackages = promptedTarget;
}
const versionBit = version ? ` (${version})` : "";
const sourceBit =
source === "devDependencies" ? chalk.white(" (dev)") : "";
// Target version selection
let targetVersion =
flags.dependency && parseDependency(flags.dependency).version;
return {
name: `${packageName}${versionBit}${sourceBit}`,
value: packageName,
checked: !!version,
};
}),
},
]);
if (!targetVersion) {
const npmVersions = npmPackageInfo.versions.reverse();
const npmDistTags = npmPackageInfo["dist-tags"];
const npmVersions = npmPackageInfo.versions.reverse();
const npmDistTags = npmPackageInfo["dist-tags"];
const highestInstalled =
!isNewDependency &&
dependencyMap[targetDependency].versions.sort(semverCompare).pop();
const highestInstalled =
!isNewDependency &&
dependencyMap[targetDependency].versions.sort(semverCompare).pop();
const availableVersions = [
...Object.entries(npmDistTags).map(([tag, version]) => ({
name: `${version} ${chalk.bold(`#${tag}`)}`,
value: version,
})),
!isNewDependency && {
name: `${highestInstalled} ${chalk.bold("Highest installed")}`,
value: highestInstalled,
},
...npmVersions.filter(
version =>
version !== highestInstalled &&
!Object.values(npmDistTags).includes(version)
),
].filter(Boolean);
const availableVersions = [
...Object.entries(npmDistTags).map(([tag, version]) => ({
name: `${version} ${chalk.bold(`#${tag}`)}`,
value: version,
})),
!isNewDependency && {
name: `${highestInstalled} ${chalk.bold("Highest installed")}`,
value: highestInstalled,
},
...npmVersions.filter(
version =>
version !== highestInstalled &&
!Object.values(npmDistTags).includes(version)
),
].filter(Boolean);
const { targetVersion: promptedTarget } = await inquirer.prompt([
{
type: "semverList",
name: "targetVersion",
message: "Select version to install:",
pageSize: 10,
choices: availableVersions,
},
]);
const { targetVersion } = await inquirer.prompt([
{
type: "semverList",
name: "targetVersion",
message: "Select version to install:",
pageSize: 10,
choices: availableVersions,
},
]);
targetVersion = promptedTarget;
}

@@ -303,3 +368,3 @@ perf.start();

}
} else {
} else if (!flags.newInstallsMode) {
const { targetSource } = await inquirer.prompt([

@@ -320,2 +385,8 @@ {

source = targetSource;
} else {
source = {
prod: "dependencies",
dev: "devDependencies",
peer: "peerDependencies",
}[flags.newInstallsMode];
}

@@ -358,74 +429,82 @@

const userName = (
(await runCommand("git config --get github.user", { logOutput: false })) ||
(await runCommand("whoami", { logOutput: false })) ||
"upgrade"
)
.split("\n")
.shift();
if (!flags.nonInteractive) {
const userName = (
(await runCommand("git config --get github.user", {
logOutput: false,
})) ||
(await runCommand("whoami", { logOutput: false })) ||
"upgrade"
)
.split("\n")
.shift();
const {
shouldCreateGitBranch,
shouldCreateGitCommit,
gitBranchName,
gitCommitMessage,
} = await inquirer.prompt([
{
type: "confirm",
name: "shouldCreateGitBranch",
message: "Do you want to create a new git branch for the change?",
},
{
type: "input",
name: "gitBranchName",
message: "Enter a name for your branch:",
when: ({ shouldCreateGitBranch }) => shouldCreateGitBranch,
default: sanitizeGitBranchName(
`${userName}/${targetDependency}-${targetVersion}`
),
},
{
type: "confirm",
name: "shouldCreateGitCommit",
message: "Do you want to create a new git commit for the change?",
},
{
type: "input",
name: "gitCommitMessage",
message: "Enter a git commit message:",
when: ({ shouldCreateGitCommit }) => shouldCreateGitCommit,
default: `Upgrade dependency: ${targetDependency}@${targetVersion}`,
},
]);
const {
shouldCreateGitBranch,
shouldCreateGitCommit,
gitBranchName,
gitCommitMessage,
} = await inquirer.prompt([
{
type: "confirm",
name: "shouldCreateGitBranch",
message: "Do you want to create a new git branch for the change?",
},
{
type: "input",
name: "gitBranchName",
message: "Enter a name for your branch:",
when: ({ shouldCreateGitBranch }) => shouldCreateGitBranch,
default: sanitizeGitBranchName(
`${userName}/${targetDependency}-${targetVersion}`
),
},
{
type: "confirm",
name: "shouldCreateGitCommit",
message: "Do you want to create a new git commit for the change?",
},
{
type: "input",
name: "gitCommitMessage",
message: "Enter a git commit message:",
when: ({ shouldCreateGitCommit }) => shouldCreateGitCommit,
default: `Upgrade dependency: ${targetDependency}@${targetVersion}`,
},
]);
if (shouldCreateGitBranch) {
const createCmd = `git checkout -b ${gitBranchName}`;
await runCommand(`cd ${projectDir} && ${createCmd}`, {
startMessage: `${chalk.white.bold(projectName)}: ${createCmd}`,
endMessage: chalk.green(`Branch created ✓`),
});
}
if (shouldCreateGitBranch) {
const createCmd = `git checkout -b ${gitBranchName}`;
await runCommand(`cd ${projectDir} && ${createCmd}`, {
startMessage: `${chalk.white.bold(projectName)}: ${createCmd}`,
endMessage: chalk.green(`Branch created ✓`),
});
}
if (shouldCreateGitCommit) {
const subMessage = targetPackages
.reduce((prev, depName) => {
const fromVersion =
!isNewDependency &&
dependencyMap[targetDependency].packs[depName].version;
if (shouldCreateGitCommit) {
const subMessage = targetPackages
.reduce((prev, depName) => {
const fromVersion =
!isNewDependency &&
dependencyMap[targetDependency].packs[depName].version;
if (fromVersion === targetVersion) return prev;
if (fromVersion === targetVersion) return prev;
return fromVersion
? [...prev, `* ${depName}: ${fromVersion} → ${targetVersion}`]
: [...prev, `* ${depName}: ${targetVersion}`];
}, [])
.join("\n");
return fromVersion
? [...prev, `* ${depName}: ${fromVersion} → ${targetVersion}`]
: [...prev, `* ${depName}: ${targetVersion}`];
}, [])
.join("\n");
const createCmd = `git add . && git commit -m '${gitCommitMessage}' -m '${subMessage}'`;
await runCommand(`cd ${projectDir} && ${createCmd}`, {
startMessage: `${chalk.white.bold(projectName)}: git add . && git commit`,
endMessage: chalk.green(`Commit created ✓`),
logOutput: false,
});
const createCmd = `git add . && git commit -m '${gitCommitMessage}' -m '${subMessage}'`;
await runCommand(`cd ${projectDir} && ${createCmd}`, {
startMessage: `${chalk.white.bold(
projectName
)}: git add . && git commit`,
endMessage: chalk.green(`Commit created ✓`),
logOutput: false,
});
}
} else {
process.exit();
}
};

@@ -94,2 +94,59 @@ const { default: runProgram } = require("./utils/runProgram");

});
describe("non-interactive", () => {
it("Adds the dependency via the --dependency flag", async () => {
// eslint-disable-next-line
jest.setTimeout(100000);
const projectPath = await generateProject({
name: "project-add-dependency-non-interactive",
packages: [
{ name: "sub-package-a" },
{
name: "sub-package-b",
dependencies: { treediff: "0.1.0" },
},
{ name: "sub-package-c" },
{ name: "sub-package-d", dependencies: { lodash: "0.2.0" } },
],
});
await runProgram(
projectPath,
`
❯◯ sub-package-a
◯ sub-package-b
◯ sub-package-c
◯ sub-package-d
>>> input SPACE
❯◉ sub-package-a
>>> input ENTER
Select dependency installation type for "sub-package-a"
>>> input ENTER
? Do you want to create a new git branch for the change? (Y/n)
>>> input CTRL+C
`,
{
flags: "--dependency promise-react-component@0.0.2",
}
);
expect(
await fileExists(
resolve(
projectPath,
"packages/sub-package-a/node_modules/promise-react-component/package.json"
)
),
"to be true"
);
});
});
});

@@ -50,2 +50,3 @@ const fs = require("fs-extra");

...pOptions,
git: false,
prefixPath: path.resolve(p, pOptions.moduleDirName || "packages"),

@@ -57,2 +58,8 @@ },

}
if (options.git) {
await exec(
`cd ${p} && echo "node_modules" > .gitignore && git init && git add . && git commit -nam 'initial commit'`
);
}
};

@@ -59,0 +66,0 @@

@@ -102,3 +102,3 @@ const chalk = require("chalk");

inputSequence,
{ log = !!process.env.CI || !!process.env.DEBUG } = {}
{ log = !!process.env.CI || !!process.env.DEBUG, flags = "" } = {}
) => {

@@ -114,3 +114,3 @@ const program =

const cmd = `./bin/lernaupdate ${projectPath}`;
const cmd = `./bin/lernaupdate${flags && ` ${flags}`} ${projectPath}`;
const proc = spawn(cmd, { shell: true });

@@ -117,0 +117,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc