Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Add support for remainder arguments with new `allowRemainderArguments` option in a global, bulk, and phased command configurations in `common/config/rush/command-line.json`",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/hashed-folder-copy-plugin",
"comment": "",
"type": "none"
}
],
"packageName": "@rushstack/hashed-folder-copy-plugin"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "",
"type": "none",
"packageName": "@rushstack/heft-webpack5-plugin"
}
],
"packageName": "@rushstack/heft-webpack5-plugin",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/ts-command-line",
"comment": "This change introduces enhanced support for remainder arguments in the CommandLineRemainder class. The -- separator used to delimit remainder arguments is now automatically excluded from the values array. For example, my-tool --flag -- arg1 arg2 will result in values being [\"arg1\", \"arg2\"], not [\"--\", \"arg1\", \"arg2\"].",
"type": "patch"
}
],
"packageName": "@rushstack/ts-command-line"
}
1 change: 1 addition & 0 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ export interface ICreateOperationsContext {
readonly projectConfigurations: ReadonlyMap<RushConfigurationProject, RushProjectConfiguration>;
readonly projectSelection: ReadonlySet<RushConfigurationProject>;
readonly projectsInUnknownState: ReadonlySet<RushConfigurationProject>;
readonly remainderArgs?: ReadonlyArray<string>;
readonly rushConfiguration: RushConfiguration;
}

Expand Down
6 changes: 2 additions & 4 deletions heft-plugins/heft-rspack-plugin/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
{
"extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json",
"compilerOptions": {
"lib": [
"DOM"
],
"lib": ["DOM"],
"module": "nodenext",
"moduleResolution": "nodenext"
}
}
}
4 changes: 3 additions & 1 deletion heft-plugins/heft-webpack5-plugin/src/Webpack5Plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ export default class Webpack5Plugin implements IHeftTaskPlugin<IWebpackPluginOpt
taskSession.logger.terminal
);
this._webpack = await import(webpackPackagePath);
taskSession.logger.terminal.writeDebugLine(`Using Webpack from rig package at "${webpackPackagePath}"`);
taskSession.logger.terminal.writeDebugLine(
`Using Webpack from rig package at "${webpackPackagePath}"`
);
} catch (e) {
// Fallback to bundled version if not found in rig.
this._webpack = await import(WEBPACK_PACKAGE_NAME);
Expand Down
3 changes: 3 additions & 0 deletions libraries/rush-lib/src/api/CommandLineJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface IBulkCommandJson extends IBaseCommandJson {
allowWarningsInSuccessfulBuild?: boolean;
watchForChanges?: boolean;
disableBuildCache?: boolean;
allowRemainderArguments?: boolean;
}

/**
Expand All @@ -41,6 +42,7 @@ export interface IPhasedCommandWithoutPhasesJson extends IBaseCommandJson {
enableParallelism: boolean;
allowOversubscription?: boolean;
incremental?: boolean;
allowRemainderArguments?: boolean;
}

/**
Expand All @@ -64,6 +66,7 @@ export interface IPhasedCommandJson extends IPhasedCommandWithoutPhasesJson {
export interface IGlobalCommandJson extends IBaseCommandJson {
commandKind: 'global';
shellCommand: string;
allowRemainderArguments?: boolean;
}

export type CommandJson = IBulkCommandJson | IGlobalCommandJson | IPhasedCommandJson;
Expand Down
125 changes: 125 additions & 0 deletions libraries/rush-lib/src/api/test/CommandLineConfiguration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,129 @@ describe(CommandLineConfiguration.name, () => {
expect(phase.shellCommand).toEqual('echo');
});
});

describe('allowRemainderArguments configuration', () => {
it('should accept allowRemainderArguments for bulk commands', () => {
const commandLineConfiguration: CommandLineConfiguration = new CommandLineConfiguration({
commands: [
{
commandKind: 'bulk',
name: 'test-remainder-bulk',
summary: 'Test bulk command with remainder arguments',
enableParallelism: true,
safeForSimultaneousRushProcesses: false,
allowRemainderArguments: true
}
]
});

const command = commandLineConfiguration.commands.get('test-remainder-bulk');
expect(command).toBeDefined();
expect(command?.allowRemainderArguments).toBe(true);
});

it('should accept allowRemainderArguments for global commands', () => {
const commandLineConfiguration: CommandLineConfiguration = new CommandLineConfiguration({
commands: [
{
commandKind: 'global',
name: 'test-remainder-global',
summary: 'Test global command with remainder arguments',
shellCommand: 'echo',
safeForSimultaneousRushProcesses: false,
allowRemainderArguments: true
}
]
});

const command = commandLineConfiguration.commands.get('test-remainder-global');
expect(command).toBeDefined();
expect(command?.allowRemainderArguments).toBe(true);
});

it('should accept allowRemainderArguments for phased commands', () => {
const commandLineConfiguration: CommandLineConfiguration = new CommandLineConfiguration({
commands: [
{
commandKind: 'phased',
name: 'test-remainder-phased',
summary: 'Test phased command with remainder arguments',
enableParallelism: true,
safeForSimultaneousRushProcesses: false,
phases: ['_phase:test'],
allowRemainderArguments: true
}
],
phases: [
{
name: '_phase:test'
}
]
});

const command = commandLineConfiguration.commands.get('test-remainder-phased');
expect(command).toBeDefined();
expect(command?.allowRemainderArguments).toBe(true);
});

it('should default allowRemainderArguments to false when not specified', () => {
const commandLineConfiguration: CommandLineConfiguration = new CommandLineConfiguration({
commands: [
{
commandKind: 'global',
name: 'test-no-remainder',
summary: 'Test command without remainder arguments',
shellCommand: 'echo',
safeForSimultaneousRushProcesses: false
}
]
});

const command = commandLineConfiguration.commands.get('test-no-remainder');
expect(command).toBeDefined();
expect(command?.allowRemainderArguments).toBeUndefined();
});

it('should work with both custom parameters and remainder arguments', () => {
const commandLineConfiguration: CommandLineConfiguration = new CommandLineConfiguration({
commands: [
{
commandKind: 'global',
name: 'test-mixed-params',
summary: 'Test command with both custom parameters and remainder arguments',
shellCommand: 'echo',
safeForSimultaneousRushProcesses: false,
allowRemainderArguments: true
}
],
parameters: [
{
parameterKind: 'flag',
longName: '--verbose',
associatedCommands: ['test-mixed-params'],
description: 'Enable verbose logging'
},
{
parameterKind: 'string',
longName: '--output',
argumentName: 'PATH',
associatedCommands: ['test-mixed-params'],
description: 'Output file path'
},
{
parameterKind: 'integer',
longName: '--count',
argumentName: 'NUM',
associatedCommands: ['test-mixed-params'],
description: 'Number of iterations'
}
]
});

const command = commandLineConfiguration.commands.get('test-mixed-params');
expect(command).toBeDefined();
expect(command?.allowRemainderArguments).toBe(true);
expect(command?.associatedParameters.size).toBe(3);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ export abstract class BaseScriptAction<TCommand extends Command> extends BaseRus
return;
}

// Define remainder parameter if the command allows it
if (this.command.allowRemainderArguments) {
this.defineCommandLineRemainder({
description:
'Additional command-line arguments to be passed through to the shell command or npm script'
});
}

// Use the centralized helper to create CommandLineParameter instances
defineCustomParameters(this, this.command.associatedParameters, this.customParameters);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ export class GlobalScriptAction extends BaseScriptAction<IGlobalCommandConfig> {
tsCommandLineParameter.appendToArgList(customParameterValues);
}

// Add remainder arguments if they exist
if (this.remainder) {
this.remainder.appendToArgList(customParameterValues);
}

for (let i: number = 0; i < customParameterValues.length; i++) {
let customParameterValue: string = customParameterValues[i];
customParameterValue = customParameterValue.replace(/"/g, '\\"');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> i
changedProjectsOnly,
cobuildConfiguration,
customParameters: customParametersByName,
remainderArgs: this.remainder?.values,
isIncrementalBuildAllowed: this._isIncrementalBuildAllowed,
isInitial: true,
isWatch,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ export class IPCOperationRunnerPlugin implements IPhasedCommandPlugin {
before: ShellOperationPluginName
},
async (operations: Set<Operation>, context: ICreateOperationsContext) => {
const { isWatch, isInitial } = context;
const { isWatch, isInitial, remainderArgs } = context;
if (!isWatch) {
return operations;
}

currentContext = context;

const getCustomParameterValues: (operation: Operation) => ICustomParameterValuesForOperation =
getCustomParameterValuesByOperation();
getCustomParameterValuesByOperation(remainderArgs);

for (const operation of operations) {
const { associatedPhase: phase, associatedProject: project, runner } = operation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ export class ShardedPhasedOperationPlugin implements IPhasedCommandPlugin {
}

function spliceShards(existingOperations: Set<Operation>, context: ICreateOperationsContext): Set<Operation> {
const { rushConfiguration, projectConfigurations } = context;
const { rushConfiguration, projectConfigurations, remainderArgs } = context;

const getCustomParameterValues: (operation: Operation) => ICustomParameterValuesForOperation =
getCustomParameterValuesByOperation();
getCustomParameterValuesByOperation(remainderArgs);

for (const operation of existingOperations) {
const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ export class ShellOperationRunnerPlugin implements IPhasedCommandPlugin {
operations: Set<Operation>,
context: ICreateOperationsContext
): Set<Operation> {
const { rushConfiguration, isInitial } = context;
const { rushConfiguration, isInitial, remainderArgs } = context;

const getCustomParameterValues: (operation: Operation) => ICustomParameterValuesForOperation =
getCustomParameterValuesByOperation();
getCustomParameterValuesByOperation(remainderArgs);

for (const operation of operations) {
const { associatedPhase: phase, associatedProject: project } = operation;
Expand Down Expand Up @@ -138,11 +138,17 @@ export interface ICustomParameterValuesForOperation {
/**
* Helper function to collect all parameter arguments for a phase
*/
function collectPhaseParameterArguments(phase: IPhase): string[] {
function collectPhaseParameterArguments(phase: IPhase, remainderArgs?: ReadonlyArray<string>): string[] {
const customParameterList: string[] = [];
for (const tsCommandLineParameter of phase.associatedParameters) {
tsCommandLineParameter.appendToArgList(customParameterList);
}

// Add remainder arguments if they exist
if (remainderArgs && remainderArgs.length > 0) {
customParameterList.push(...remainderArgs);
}

return customParameterList;
}

Expand All @@ -169,11 +175,12 @@ export function getCustomParameterValuesByPhase(): (phase: IPhase) => ReadonlyAr
/**
* Gets custom parameter values for an operation, filtering out any parameters that should be ignored
* based on the operation's settings.
* @param remainderArgs - Optional remainder arguments to append to parameter values
* @returns A function that returns the filtered custom parameter values and ignored parameter values for a given operation
*/
export function getCustomParameterValuesByOperation(): (
operation: Operation
) => ICustomParameterValuesForOperation {
export function getCustomParameterValuesByOperation(
remainderArgs?: ReadonlyArray<string>
): (operation: Operation) => ICustomParameterValuesForOperation {
const customParametersByPhase: Map<IPhase, string[]> = new Map();

function getCustomParameterValuesForOp(operation: Operation): ICustomParameterValuesForOperation {
Expand All @@ -185,7 +192,7 @@ export function getCustomParameterValuesByOperation(): (
// No filtering needed - use the cached parameter list for efficiency
let customParameterList: string[] | undefined = customParametersByPhase.get(phase);
if (!customParameterList) {
customParameterList = collectPhaseParameterArguments(phase);
customParameterList = collectPhaseParameterArguments(phase, remainderArgs);
customParametersByPhase.set(phase, customParameterList);
}

Expand All @@ -210,6 +217,11 @@ export function getCustomParameterValuesByOperation(): (
);
}

// Add remainder arguments to the filtered values (they can't be ignored individually)
if (remainderArgs && remainderArgs.length > 0) {
filteredParameterValues.push(...remainderArgs);
}

return {
parameterValues: filteredParameterValues,
ignoredParameterValues
Expand Down
Loading