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": "Allow a monorepo to provide custom config file templates for the `rush init-subspace` command",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
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 @@ -1544,6 +1544,7 @@ export class SubspacesConfiguration {
readonly preventSelectingAllSubspaces: boolean;
static requireValidSubspaceName(subspaceName: string, splitWorkspaceCompatibility?: boolean): void;
readonly splitWorkspaceCompatibility: boolean;
readonly subspaceInitAssetsFolder?: string;
readonly subspaceJsonFilePath: string;
readonly subspaceNames: ReadonlySet<string>;
// (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
*/
"subspacesEnabled": false,

/**
* the starting assets folder relative to rush.json location you want to use when init a subspace
*/
// "subspaceInitAssetsFolder": "",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this design, it seems that a given monorepo can have at most one set of custom file templates to use with rush init-subspace. Therefore, why do we need to configure its location?

We could instead designate a standard location, e.g. common/config-templates/rush-init-subspace. Then everyone will know that is where to find it, without having to read subspaces.json to find the particular location for a particular repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks! I’ll update it accordingly.

Copy link
Member

@iclanton iclanton Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe common/templates/config. We could eventually have other kinds of templates (project templates?)


/**
* (DEPRECATED) This is a temporary workaround for migrating from an earlier prototype
* of this feature: https://github.com/microsoft/rushstack/pull/3481
Expand Down
7 changes: 7 additions & 0 deletions libraries/rush-lib/src/api/SubspacesConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface ISubspacesConfigurationJson {
splitWorkspaceCompatibility?: boolean;
preventSelectingAllSubspaces?: boolean;
subspaceNames: string[];
subspaceInitAssetsFolder?: string;
}

/**
Expand Down Expand Up @@ -60,11 +61,17 @@ export class SubspacesConfiguration {
*/
public readonly subspaceNames: ReadonlySet<string>;

/**
* rush-init Subspace assets location used during `rush init-subspace`
*/
public readonly subspaceInitAssetsFolder?: string;

private constructor(configuration: Readonly<ISubspacesConfigurationJson>, subspaceJsonFilePath: string) {
this.subspaceJsonFilePath = subspaceJsonFilePath;
this.subspacesEnabled = configuration.subspacesEnabled;
this.splitWorkspaceCompatibility = !!configuration.splitWorkspaceCompatibility;
this.preventSelectingAllSubspaces = !!configuration.preventSelectingAllSubspaces;
this.subspaceInitAssetsFolder = configuration.subspaceInitAssetsFolder;
const subspaceNames: Set<string> = new Set();
for (const subspaceName of configuration.subspaceNames) {
SubspacesConfiguration.requireValidSubspaceName(subspaceName, this.splitWorkspaceCompatibility);
Expand Down
15 changes: 12 additions & 3 deletions libraries/rush-lib/src/cli/actions/InitSubspaceAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { type ISubspacesConfigurationJson, SubspacesConfiguration } from '../../
import { Async, FileSystem, JsonFile } from '@rushstack/node-core-library';
import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal';
import { copyTemplateFileAsync } from '../../utilities/templateUtilities';
import * as path from 'path';

export class InitSubspaceAction extends BaseRushAction {
private readonly _subspaceNameParameter: IRequiredCommandLineStringParameter;
Expand Down Expand Up @@ -58,7 +59,10 @@ export class InitSubspaceAction extends BaseRushAction {
}

const subspaceConfigPath: string = `${this.rushConfiguration.commonFolder}/config/subspaces/${newSubspaceName}`;
const assetsSubfolder: string = `${assetsFolderPath}/rush-init`;
const defaultAssetsSubfolder: string = `${assetsFolderPath}/rush-init`;
const userDefinedAssetsFolder: string | undefined = subspacesConfiguration.subspaceInitAssetsFolder
? `${this.rushConfiguration.rushJsonFolder}/${subspacesConfiguration.subspaceInitAssetsFolder}`
: undefined;
const templateFilePaths: string[] = [
'[dot]npmrc',
'.pnpmfile.cjs',
Expand All @@ -70,9 +74,14 @@ export class InitSubspaceAction extends BaseRushAction {
await Async.forEachAsync(
templateFilePaths,
async (templateFilePath) => {
const sourcePath: string = `${assetsSubfolder}/common/config/rush/${templateFilePath}`;
const defaultAssetSourcePath: string = `${defaultAssetsSubfolder}/common/config/rush/${templateFilePath}`;
const destinationPath: string = `${subspaceConfigPath}/${templateFilePath.replace('[dot]', '.')}`;
await copyTemplateFileAsync(sourcePath, destinationPath, true);
await copyTemplateFileAsync(defaultAssetSourcePath, destinationPath, true);
if (userDefinedAssetsFolder) {
// if user provided their own assets file for subspace initiation
// we just copy and overwrite the default files
await copyTemplateFileAsync(userDefinedAssetsFolder, destinationPath, true);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach seems like it will (1) try to copy a file that doesn't exist and (2) print a notice like Overwriting: .pnpmfile.js when really it is just overwriting its own file that it wrote on line 79.

Both issues could be avoided using FileSystem.existsAsync().

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, userDefinedAssetsFolder is the path of a directory. Shouldn't copyTemplateFileAsync() receive a file path?

Please fill out the How it was tested PR notes 😸

}
},
{ concurrency: 10 }
);
Expand Down
4 changes: 4 additions & 0 deletions libraries/rush-lib/src/schemas/subspaces.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
"items": {
"type": "string"
}
},
"subspaceInitAssetsFolder": {
"description": "Where assets located for init a new subspace",
"type": "string"
}
}
}
Loading