Skip to content

Commit 293682c

Browse files
authored
[rush] support build/rebuild command in rush plugin (#5163)
* feat: support build/rebuild command in rush plugin * chore: remove test only * chore: create rush change * chore: rename internal variable * fix: error msg
1 parent 1394340 commit 293682c

46 files changed

Lines changed: 704 additions & 61 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/rush",
5+
"comment": "Fix an issue where build/rebuild can not be defined in a rush plugin command line configuration",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush"
10+
}

libraries/rush-lib/src/api/CommandLineConfiguration.ts

Lines changed: 75 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -456,27 +456,28 @@ export class CommandLineConfiguration {
456456
buildCommandOriginalPhases = buildCommand.originalPhases;
457457
this.commands.set(buildCommand.name, buildCommand);
458458
}
459+
}
459460

460-
if (!this.commands.has(RushConstants.rebuildCommandName)) {
461-
// If a rebuild command was not specified in the config file, add the default rebuild command
462-
if (!buildCommandPhases || !buildCommandOriginalPhases) {
463-
throw new Error(`Phases for the "${RushConstants.buildCommandName}" were not found.`);
464-
}
465-
466-
const rebuildCommand: IPhasedCommandConfig = {
467-
...DEFAULT_REBUILD_COMMAND_JSON,
468-
commandKind: RushConstants.phasedCommandKind,
469-
isSynthetic: true,
470-
phases: buildCommandPhases,
471-
disableBuildCache: DEFAULT_REBUILD_COMMAND_JSON.disableBuildCache,
472-
associatedParameters: buildCommand.associatedParameters, // rebuild should share build's parameters in this case,
473-
originalPhases: buildCommandOriginalPhases,
474-
watchPhases: new Set(),
475-
alwaysWatch: false,
476-
alwaysInstall: undefined
477-
};
478-
this.commands.set(rebuildCommand.name, rebuildCommand);
461+
const buildCommand: Command | undefined = this.commands.get(RushConstants.buildCommandName);
462+
if (buildCommand && !this.commands.has(RushConstants.rebuildCommandName)) {
463+
// If a rebuild command was not specified in the config file, add the default rebuild command
464+
if (!buildCommandPhases || !buildCommandOriginalPhases) {
465+
throw new Error(`Phases for the "${RushConstants.buildCommandName}" were not found.`);
479466
}
467+
468+
const rebuildCommand: IPhasedCommandConfig = {
469+
...DEFAULT_REBUILD_COMMAND_JSON,
470+
commandKind: RushConstants.phasedCommandKind,
471+
isSynthetic: true,
472+
phases: buildCommandPhases,
473+
disableBuildCache: DEFAULT_REBUILD_COMMAND_JSON.disableBuildCache,
474+
associatedParameters: buildCommand.associatedParameters, // rebuild should share build's parameters in this case,
475+
originalPhases: buildCommandOriginalPhases,
476+
watchPhases: new Set(),
477+
alwaysWatch: false,
478+
alwaysInstall: undefined
479+
};
480+
this.commands.set(rebuildCommand.name, rebuildCommand);
480481
}
481482

482483
const parametersJson: ICommandLineJson['parameters'] = commandLineJson?.parameters;
@@ -525,8 +526,8 @@ export class CommandLineConfiguration {
525526
if (!associatedCommand) {
526527
throw new Error(
527528
`${RushConstants.commandLineFilename} defines a parameter "${normalizedParameter.longName}" ` +
528-
`that is associated with a command "${associatedCommandName}" that does not exist or does ` +
529-
'not support custom parameters.'
529+
`that is associated with a command "${associatedCommandName}" that is not defined in ` +
530+
'this file.'
530531
);
531532
} else {
532533
associatedCommand.associatedParameters.add(normalizedParameter);
@@ -597,6 +598,42 @@ export class CommandLineConfiguration {
597598
cycleFreePhases.add(phase);
598599
}
599600

601+
private static _applyBuildCommandDefaults(commandLineJson: ICommandLineJson): void {
602+
// merge commands specified in command-line.json and default (re)build settings
603+
// Ensure both build commands are included and preserve any other commands specified
604+
if (commandLineJson?.commands) {
605+
for (let i: number = 0; i < commandLineJson.commands.length; i++) {
606+
const command: CommandJson = commandLineJson.commands[i];
607+
608+
// Determine if we have a set of default parameters
609+
let commandDefaultDefinition: CommandJson | {} = {};
610+
switch (command.commandKind) {
611+
case RushConstants.phasedCommandKind:
612+
case RushConstants.bulkCommandKind: {
613+
switch (command.name) {
614+
case RushConstants.buildCommandName: {
615+
commandDefaultDefinition = DEFAULT_BUILD_COMMAND_JSON;
616+
break;
617+
}
618+
619+
case RushConstants.rebuildCommandName: {
620+
commandDefaultDefinition = DEFAULT_REBUILD_COMMAND_JSON;
621+
break;
622+
}
623+
}
624+
break;
625+
}
626+
}
627+
628+
// Merge the default parameters into the repo-specified parameters
629+
commandLineJson.commands[i] = {
630+
...commandDefaultDefinition,
631+
...command
632+
};
633+
}
634+
}
635+
}
636+
600637
/**
601638
* Load the command-line.json configuration file from the specified path. Note that this
602639
* does not include the default build settings. This option is intended to be used to load
@@ -616,7 +653,17 @@ export class CommandLineConfiguration {
616653
}
617654

618655
if (commandLineJson) {
619-
return new CommandLineConfiguration(commandLineJson, { doNotIncludeDefaultBuildCommands: true });
656+
this._applyBuildCommandDefaults(commandLineJson);
657+
const hasBuildCommand: boolean = !!commandLineJson.commands?.some(
658+
(command) => command.name === RushConstants.buildCommandName
659+
);
660+
const hasRebuildCommand: boolean = !!commandLineJson.commands?.some(
661+
(command) => command.name === RushConstants.rebuildCommandName
662+
);
663+
664+
return new CommandLineConfiguration(commandLineJson, {
665+
doNotIncludeDefaultBuildCommands: !(hasBuildCommand || hasRebuildCommand)
666+
});
620667
} else {
621668
return undefined;
622669
}
@@ -627,7 +674,10 @@ export class CommandLineConfiguration {
627674
* settings. If the file does not exist, then a default instance is returned.
628675
* If the file contains errors, then an exception is thrown.
629676
*/
630-
public static loadFromFileOrDefault(jsonFilePath?: string): CommandLineConfiguration {
677+
public static loadFromFileOrDefault(
678+
jsonFilePath?: string,
679+
doNotIncludeDefaultBuildCommands?: boolean
680+
): CommandLineConfiguration {
631681
let commandLineJson: ICommandLineJson | undefined = undefined;
632682
if (jsonFilePath) {
633683
try {
@@ -641,41 +691,13 @@ export class CommandLineConfiguration {
641691
// merge commands specified in command-line.json and default (re)build settings
642692
// Ensure both build commands are included and preserve any other commands specified
643693
if (commandLineJson?.commands) {
644-
for (let i: number = 0; i < commandLineJson.commands.length; i++) {
645-
const command: CommandJson = commandLineJson.commands[i];
646-
647-
// Determine if we have a set of default parameters
648-
let commandDefaultDefinition: CommandJson | {} = {};
649-
switch (command.commandKind) {
650-
case RushConstants.phasedCommandKind:
651-
case RushConstants.bulkCommandKind: {
652-
switch (command.name) {
653-
case RushConstants.buildCommandName: {
654-
commandDefaultDefinition = DEFAULT_BUILD_COMMAND_JSON;
655-
break;
656-
}
657-
658-
case RushConstants.rebuildCommandName: {
659-
commandDefaultDefinition = DEFAULT_REBUILD_COMMAND_JSON;
660-
break;
661-
}
662-
}
663-
break;
664-
}
665-
}
666-
667-
// Merge the default parameters into the repo-specified parameters
668-
commandLineJson.commands[i] = {
669-
...commandDefaultDefinition,
670-
...command
671-
};
672-
}
694+
this._applyBuildCommandDefaults(commandLineJson);
673695

674696
CommandLineConfiguration._jsonSchema.validateObject(commandLineJson, jsonFilePath);
675697
}
676698
}
677699

678-
return new CommandLineConfiguration(commandLineJson, { doNotIncludeDefaultBuildCommands: false });
700+
return new CommandLineConfiguration(commandLineJson, { doNotIncludeDefaultBuildCommands });
679701
}
680702

681703
public prependAdditionalPathFolder(pathFolder: string): void {

libraries/rush-lib/src/cli/RushCommandLineParser.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export class RushCommandLineParser extends CommandLineParser {
8585
private readonly _rushOptions: IRushCommandLineParserOptions;
8686
private readonly _terminalProvider: ConsoleTerminalProvider;
8787
private readonly _terminal: Terminal;
88+
private readonly _autocreateBuildCommand: boolean;
8889

8990
public constructor(options?: Partial<IRushCommandLineParserOptions>) {
9091
super({
@@ -150,18 +151,28 @@ export class RushCommandLineParser extends CommandLineParser {
150151
rushGlobalFolder: this.rushGlobalFolder
151152
});
152153

153-
this._populateActions();
154-
155154
const pluginCommandLineConfigurations: ICustomCommandLineConfigurationInfo[] =
156155
this.pluginManager.tryGetCustomCommandLineConfigurationInfos();
156+
157+
const hasBuildCommandInPlugin: boolean = pluginCommandLineConfigurations.some((x) =>
158+
x.commandLineConfiguration.commands.has(RushConstants.buildCommandName)
159+
);
160+
161+
// If the plugin has a build command, we don't need to autocreate the default build command.
162+
this._autocreateBuildCommand = !hasBuildCommandInPlugin;
163+
164+
this._populateActions();
165+
157166
for (const { commandLineConfiguration, pluginLoader } of pluginCommandLineConfigurations) {
158167
try {
159168
this._addCommandLineConfigActions(commandLineConfiguration);
160169
} catch (e) {
161-
this._terminal.writeErrorLine(
162-
`Error from plugin ${pluginLoader.pluginName} by ${pluginLoader.packageName}: ${(
163-
e as Error
164-
).toString()}`
170+
this._reportErrorAndSetExitCode(
171+
new Error(
172+
`Error from plugin ${pluginLoader.pluginName} by ${pluginLoader.packageName}: ${(
173+
e as Error
174+
).toString()}`
175+
)
165176
);
166177
}
167178
}
@@ -338,8 +349,13 @@ export class RushCommandLineParser extends CommandLineParser {
338349
);
339350
}
340351

341-
const commandLineConfiguration: CommandLineConfiguration =
342-
CommandLineConfiguration.loadFromFileOrDefault(commandLineConfigFilePath);
352+
// If a build action is already added by a plugin, we don't want to add a default "build" script
353+
const doNotIncludeDefaultBuildCommands: boolean = !this._autocreateBuildCommand;
354+
355+
const commandLineConfiguration: CommandLineConfiguration = CommandLineConfiguration.loadFromFileOrDefault(
356+
commandLineConfigFilePath,
357+
doNotIncludeDefaultBuildCommands
358+
);
343359
this._addCommandLineConfigActions(commandLineConfiguration);
344360
}
345361

0 commit comments

Comments
 (0)