Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): add add-caching command #3350

Merged
merged 3 commits into from Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions core/lerna/commands/add-caching/command.js
@@ -0,0 +1,19 @@
// @ts-check

"use strict";

/**
* @see https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module
*/
exports.command = "add-caching";

exports.describe = "Interactive prompt to generate task runner configuration";

exports.builder = (yargs) => {
return yargs;
};

exports.handler = function handler(argv) {
// eslint-disable-next-line global-require
return require(".")(argv);
};
158 changes: 158 additions & 0 deletions core/lerna/commands/add-caching/index.js
@@ -0,0 +1,158 @@
// @ts-check

"use strict";

const { Command } = require("@lerna/command");
const { writeJsonFile, readJsonFile, workspaceRoot, joinPathFragments } = require("@nrwl/devkit");
const inquirer = require("inquirer");
const log = require("npmlog");

module.exports = factory;

function factory(argv) {
return new AddCachingCommand(argv);
}

class AddCachingCommand extends Command {
constructor(argv) {
super(argv, { skipValidations: true });
}

initialize() {
if (this.options.useNx === false) {
this.logger.error(
"add-caching",
"The `add-caching` command is only available when using the Nx task runner (do not set `useNx` to `false` in `lerna.json`)"
);
// eslint-disable-next-line no-process-exit
process.exit(1);
}

const packages = this.packageGraph?.rawPackageList || [];
const uniqueScriptNames = new Set();
for (const pkg of packages) {
for (const scriptName of Object.keys(pkg.scripts || {})) {
uniqueScriptNames.add(scriptName);
}
}
this.uniqueScriptNames = Array.from(uniqueScriptNames);
}

async execute() {
this.logger.info(
"add-caching",
"Please answer the following questions about the scripts found in your workspace in order to generate task runner configuration"
);
process.stdout.write("\n");

log.pause();

const { targetDefaults } = await inquirer.prompt([
{
type: "checkbox",
name: "targetDefaults",
message: "Which of the following scripts need to be run in deterministic/topoglogical order?\n",
JamesHenry marked this conversation as resolved.
Show resolved Hide resolved
choices: this.uniqueScriptNames,
},
]);

const { cacheableOperations } = await inquirer.prompt([
{
type: "checkbox",
name: "cacheableOperations",
message:
"Which of the following scripts are cacheable? (Produce the same output given the same input, e.g. build, test and lint usually are, serve and start are not)\n",
choices: this.uniqueScriptNames,
},
]);

const scriptOutputs = {};

for (const scriptName of cacheableOperations) {
// eslint-disable-next-line no-await-in-loop
scriptOutputs[scriptName] = await inquirer.prompt([
{
type: "input",
name: scriptName,
message: `Does the "${scriptName}" script create any outputs? If not, leave blank, otherwise provide a path relative to a project root (e.g. dist, lib, build, coverage)\n`,
},
]);
}

log.resume();

process.stdout.write("\n");

this.convertAnswersToNxConfig({ cacheableOperations, targetDefaults, scriptOutputs });

this.logger.success("add-caching", "Successfully updated task runner configuration in `nx.json`");

this.logger.info(
"add-caching",
"Learn more about task runner configuration here: https://lerna.js.org/docs/concepts/task-pipeline-configuration"
JamesHenry marked this conversation as resolved.
Show resolved Hide resolved
);
this.logger.info(
"add-caching",
"Note that the legacy task runner options of --sort, --no-sort and --parallel no longer apply. Learn more here: https://lerna.js.org/docs/recipes/using-lerna-powered-by-nx-to-run-tasks"
);
}

convertAnswersToNxConfig(answers) {
const nxJsonPath = joinPathFragments(workspaceRoot, "nx.json");
let nxJson = {};
try {
nxJson = readJsonFile(nxJsonPath);
// eslint-disable-next-line no-empty
} catch {}

nxJson.tasksRunnerOptions = nxJson.tasksRunnerOptions || {};
nxJson.tasksRunnerOptions.default = nxJson.tasksRunnerOptions.default || {};
nxJson.tasksRunnerOptions.default.runner =
nxJson.tasksRunnerOptions.default.runner || "nx/tasks-runners/default";
nxJson.tasksRunnerOptions.default.options = nxJson.tasksRunnerOptions.default.options || {};

if (nxJson.tasksRunnerOptions.default.options.cacheableOperations) {
this.logger.warn(
"add-caching",
"The `tasksRunnerOptions.default.cacheableOperations` property already exists in `nx.json` and will be overwritten by your answers"
);
}

nxJson.tasksRunnerOptions.default.options.cacheableOperations = answers.cacheableOperations;

if (nxJson.targetDefaults) {
this.logger.warn(
"add-caching",
"The `targetDefaults` property already exists in `nx.json` and will be overwritten by your answers"
);
}

nxJson.targetDefaults = nxJson.targetDefaults || {};

for (const scriptName of answers.targetDefaults) {
nxJson.targetDefaults[scriptName] = nxJson.targetDefaults[scriptName] || {};
nxJson.targetDefaults[scriptName] = { dependsOn: [`^${scriptName}`] };
}

for (const [scriptName, scriptAnswerData] of Object.entries(answers.scriptOutputs)) {
if (!scriptAnswerData[scriptName]) {
// eslint-disable-next-line no-continue
continue;
}
nxJson.targetDefaults[scriptName] = nxJson.targetDefaults[scriptName] || {};
nxJson.targetDefaults[scriptName].outputs = [`{projectRoot}/${scriptAnswerData[scriptName]}`];
}

writeJsonFile(nxJsonPath, nxJson);
}

// eslint-disable-next-line class-methods-use-this
normalizePathInput(pathInput) {
if (pathInput.startsWith("/")) {
return pathInput.substring(1);
}
return pathInput;
}
}

module.exports.AddCachingCommand = AddCachingCommand;
2 changes: 2 additions & 0 deletions core/lerna/index.js
Expand Up @@ -21,6 +21,7 @@ const runCmd = require("@lerna/run/command");
const versionCmd = require("@lerna/version/command");

const repairCmd = require("./commands/repair/command");
const addCachingCmd = require("./commands/add-caching/command");

const pkg = require("./package.json");

Expand All @@ -34,6 +35,7 @@ function main(argv) {
// @ts-ignore
return cli()
.command(addCmd)
.command(addCachingCmd)
.command(bootstrapCmd)
.command(changedCmd)
.command(cleanCmd)
Expand Down
1 change: 1 addition & 0 deletions core/lerna/package.json
Expand Up @@ -61,6 +61,7 @@
"@lerna/version": "file:../../commands/version",
"@nrwl/devkit": ">=14.8.1 < 16",
"import-local": "^3.0.2",
"inquirer": "^8.2.4",
JamesHenry marked this conversation as resolved.
Show resolved Hide resolved
"npmlog": "^6.0.2",
"nx": ">=14.8.1 < 16",
"typescript": "^3 || ^4"
Expand Down
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.