diff --git a/README.md b/README.md index cf37ad6..5f0373e 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,10 @@ Install additional dependencies with a single click straight from the scaffoldin ![Additional dependency step](./assets/images/scaffolding-additional-step.png) +When you can set the additional dependencies it's also possible to automatically create the node version manager file with the version of Node.js used when creating the project. These settings are available in the extension settings. + +![Additional dependency step node version](./assets/images/scaffolding-additional-step-node-version.png) + [Check out our docs for more details](https://github.com/pnp/vscode-viva/wiki/5.2-Scaffolding#1-scaffold-a-new-spfx-project) ### 6️⃣ Sign in to your tenant & retrieve environment details @@ -272,7 +276,17 @@ Check it out in action 👇 By default, the SharePoint Framework Toolkit will use the Node.js version that is installed on your machine. If you want to use a different version, you can use a Node.js Version Manager such as [nvm](https://github.com/nvm-sh/nvm) or [nvs](https://github.com/jasongin/nvs). The SharePoint Framework Toolkit will detect the preferred version of Node.js if a `.nvmrc` file is present in the root of your project, and will use that version for all the actions. -You can use the settings to change which Node.js version manager you want to use. You may choose between `nvm` and `nvs`. If you wish to avoid using a Node.js version manager, you can set the value to `none` +It's possible to use the settings to change which Node.js version manager you want to use. You may choose between `nvm` and `nvs`. If you wish to avoid using a Node.js version manager, you can set the value to `none` + +![Settings Node version manager](./assets/images/settings-node-version-manager.png) + +Other than selecting the Node.js version manager you may also select which file should be used to store the Node.js version. By default, the extension will use `.nvmrc` file, but you may change it to `.node-version` if you are using `nvs`. + +![Settings Node version file](./assets/images/settings-node-version-manager-file.png) + +It is also possible to set the default behavior when you're about to scaffold a new project. To do so there is a specific setting named `Create Node Version File Default Value`. + +![Settings Node version file default value](./assets/images/settings-node-version-manager-file-default-value.png) ### 1️⃣3️⃣ SPFx Toolkit GitHub Chat Participant diff --git a/assets/images/scaffolding-additional-step-node-version.png b/assets/images/scaffolding-additional-step-node-version.png new file mode 100644 index 0000000..8513840 Binary files /dev/null and b/assets/images/scaffolding-additional-step-node-version.png differ diff --git a/assets/images/settings-node-version-manager-file-default-value.png b/assets/images/settings-node-version-manager-file-default-value.png new file mode 100644 index 0000000..2f1ccf7 Binary files /dev/null and b/assets/images/settings-node-version-manager-file-default-value.png differ diff --git a/assets/images/settings-node-version-manager-file.png b/assets/images/settings-node-version-manager-file.png new file mode 100644 index 0000000..fd9c968 Binary files /dev/null and b/assets/images/settings-node-version-manager-file.png differ diff --git a/assets/images/settings-node-version-manager.png b/assets/images/settings-node-version-manager.png new file mode 100644 index 0000000..ac550e8 Binary files /dev/null and b/assets/images/settings-node-version-manager.png differ diff --git a/data/sp-dev-fx-samples.json b/data/sp-dev-fx-samples.json index 00df3d3..8b11f46 100644 --- a/data/sp-dev-fx-samples.json +++ b/data/sp-dev-fx-samples.json @@ -1,3 +1,4 @@ + { "samples": [ { diff --git a/package.json b/package.json index 4beaca5..635697a 100644 --- a/package.json +++ b/package.json @@ -186,6 +186,22 @@ "type": "boolean", "default": true, "description": "Show the tenant-wide extensions in the account view." + }, + "spfx-toolkit.createNodeVersionFileDefaultValue": { + "title": "Default value for the Node version file option", + "type": "boolean", + "default": false, + "description": "The default value for the new project's setting for creating the Node version file. If checked the default selected value will be YES." + }, + "spfx-toolkit.nodeVersionManagerFile": { + "title": "Node version manager file to be used", + "type": "string", + "default": ".nvmrc", + "enum": [ + ".nvmrc", + ".node-version" + ], + "description": "The file to be used to store the Node version for the selected Node version manager. Remember that the .node-version file is supported only while using nvs." } } }, diff --git a/src/constants/ProjectFileContent.ts b/src/constants/ProjectFileContent.ts index ba5a945..c673606 100644 --- a/src/constants/ProjectFileContent.ts +++ b/src/constants/ProjectFileContent.ts @@ -5,4 +5,6 @@ export enum ProjectFileContent { installReusablePropertyPaneControls = 'install-spfx-property-controls', installReusableReactControls = 'install-spfx-controls-react', installPnPJs = 'install-pnpjs', + createNVMRCFile = 'create-nvmrc-file', + createNodeVersionFile = 'create-node-version-file', } \ No newline at end of file diff --git a/src/constants/WebviewCommand.ts b/src/constants/WebviewCommand.ts index af04b04..800e2df 100644 --- a/src/constants/WebviewCommand.ts +++ b/src/constants/WebviewCommand.ts @@ -5,6 +5,10 @@ export const WebviewCommand = { folderPath: 'folder-path', validateSolutionName: 'validate-solution-name', validateComponentName: 'validate-component-name', + createNodeVersionFileDefaultValue: 'should-create-node-version-file', + nodeVersionManager: 'node-version-manager', + nodeVersionManagerFile: 'node-version-manager-file', + createNodeVersionManagerFile: 'node-version-manager-file', }, toVSCode: { useSample: 'use-sample', @@ -17,5 +21,8 @@ export const WebviewCommand = { validateComponentName: 'validate-component-name', addSpfxComponent: 'add-spfx-component', createAppReg: 'create-app-reg', + createNodeVersionFileDefaultValue: 'should-create-node-version-file', + nodeVersionManager: 'node-version-manager', + nodeVersionManagerFile: 'node-version-manager-file', } }; \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index bbcfc94..b532c01 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -63,6 +63,18 @@ export async function activate(context: vscode.ExtensionContext) { if (fileContents.indexOf(ProjectFileContent.installPnPJs) > -1) { await TerminalCommandExecuter.runCommand('npm install @pnp/sp @pnp/graph --save', [], terminalTitle, terminalIcon); } + + // If either of the following strings are found in the project file, run the command to get the node version + if (fileContents.indexOf(ProjectFileContent.createNVMRCFile) > -1 || fileContents.indexOf(ProjectFileContent.createNodeVersionFile) > -1) { + let nodeVersionCommand = 'node --version > '; + if (fileContents.indexOf(ProjectFileContent.createNVMRCFile) > -1) { + nodeVersionCommand += '.nvmrc'; + } + else if (fileContents.indexOf(ProjectFileContent.createNodeVersionFile) > -1) { + nodeVersionCommand += '.node-version'; + } + await TerminalCommandExecuter.runCommand(nodeVersionCommand, [], terminalTitle, terminalIcon); + } } } }); diff --git a/src/models/SpfxScaffoldCommandInput.ts b/src/models/SpfxScaffoldCommandInput.ts index 3e15091..8ed4bb5 100644 --- a/src/models/SpfxScaffoldCommandInput.ts +++ b/src/models/SpfxScaffoldCommandInput.ts @@ -8,4 +8,7 @@ export interface SpfxScaffoldCommandInput extends SpfxAddComponentCommandInput { shouldInstallReusablePropertyPaneControls: boolean; shouldInstallReusableReactControls: boolean; shouldInstallPnPJs: boolean; + shouldCreateNodeVersionFile: boolean; + nodeVersionManagerFile: '.nvmrc' | '.node-version'; + nodeVersionManager: 'nvm' | 'nvs' | 'none'; } \ No newline at end of file diff --git a/src/services/actions/Scaffolder.ts b/src/services/actions/Scaffolder.ts index 8ee79e1..29bd997 100644 --- a/src/services/actions/Scaffolder.ts +++ b/src/services/actions/Scaffolder.ts @@ -11,7 +11,7 @@ import * as glob from 'fast-glob'; import { Extension } from '../dataType/Extension'; import download from 'github-directory-downloader/esm'; import { CliExecuter } from '../executeWrappers/CliCommandExecuter'; -import { getPlatform } from '../../utils'; +import { getExtensionSettings, getPlatform } from '../../utils'; import { PnPWebview } from '../../webview/PnPWebview'; import { Executer } from '../executeWrappers/CommandExecuter'; import { TeamsToolkitIntegration } from '../dataType/TeamsToolkitIntegration'; @@ -147,6 +147,45 @@ export class Scaffolder { PnPWebview.postMessage(WebviewCommand.toWebview.validateComponentName, true); } + /** + * Returns the value of the createNodeVersionFileDefaultValue setting and sends it to the webview. + */ + public static async createNodeVersionFileDefaultValue() { + const value = getExtensionSettings( + 'createNodeVersionFileDefaultValue', + false + ); + + PnPWebview.postMessage( + WebviewCommand.toWebview.createNodeVersionFileDefaultValue, + value + ); + } + + /** + * Returns the value of the nodeVersionManagerFile setting and sends it to the webview. + */ + public static async nodeVersionManagerFile() { + const value = getExtensionSettings('nodeVersionManagerFile', '.nvmrc'); + + PnPWebview.postMessage( + WebviewCommand.toWebview.createNodeVersionManagerFile, + value + ); + } + + /** + * Returns the value of the nodeVersionManager setting and sends it to the webview. + */ + public static async nodeVersionManager() { + const value = getExtensionSettings('nodeVersionManager', 'nvm'); + + PnPWebview.postMessage( + WebviewCommand.toWebview.nodeVersionManager, + value + ); + } + /** * Scaffold method for creating a new project. * @param input - The input for the scaffold command. @@ -225,6 +264,31 @@ export class Scaffolder { content += ` ${ProjectFileContent.installPnPJs}`; } + if (newSolutionInput.shouldCreateNodeVersionFile) { + switch (newSolutionInput.nodeVersionManager) { + case 'nvm': + // If the node version manager is nvm, create the .nvmrc file even if the user has selected .node-version + content += ` ${ProjectFileContent.createNVMRCFile}`; + break; + case 'nvs': + // If the node version manager is nvs, create the file based on the user's settings + switch (newSolutionInput.nodeVersionManagerFile) { + case '.nvmrc': + content += ` ${ProjectFileContent.createNVMRCFile}`; + break; + case '.node-version': + content += ` ${ProjectFileContent.createNodeVersionFile}`; + break; + } + break; + // If the node version manager is none, do not create any file + case 'none': + // By default, do not create any file + default: + break; + } + } + Scaffolder.createProjectFileAndOpen(newFolderPath, content); } else { PnPWebview.close(); diff --git a/src/webview/PnPWebview.ts b/src/webview/PnPWebview.ts index f8134cf..0205372 100644 --- a/src/webview/PnPWebview.ts +++ b/src/webview/PnPWebview.ts @@ -156,6 +156,15 @@ export class PnPWebview { case WebviewCommand.toVSCode.createAppReg: EntraAppRegistration.createEntraAppRegistration(); break; + case WebviewCommand.toVSCode.createNodeVersionFileDefaultValue: + Scaffolder.createNodeVersionFileDefaultValue(); + break; + case WebviewCommand.toVSCode.nodeVersionManagerFile: + Scaffolder.nodeVersionManagerFile(); + break; + case WebviewCommand.toVSCode.nodeVersionManager: + Scaffolder.nodeVersionManager(); + break; } }); } diff --git a/src/webview/view/components/forms/spfxProject/AdditionalStep.tsx b/src/webview/view/components/forms/spfxProject/AdditionalStep.tsx index 9c001cc..2ba7ce7 100644 --- a/src/webview/view/components/forms/spfxProject/AdditionalStep.tsx +++ b/src/webview/view/components/forms/spfxProject/AdditionalStep.tsx @@ -2,7 +2,8 @@ import { VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react'; import * as React from 'react'; import { StepHeader } from './StepHeader'; import { PackageSelector } from './PackageSelector'; - +import { WebviewCommand } from '../../../../../constants'; +import { Messenger } from '@estruyf/vscode/dist/client'; interface AdditionalStepProps { shouldRunInit: boolean; @@ -13,6 +14,11 @@ interface AdditionalStepProps { setShouldInstallReusableReactControls: (value: boolean) => void; shouldInstallPnPJs: boolean; setShouldInstallPnPJs: (value: boolean) => void; + shouldCreateNodeVersionFile: boolean; + setShouldCreateNodeVersionFile: (value: boolean) => void; + nodeVersionManager: 'nvm' | 'nvs' | 'none'; + setNodeVersionManager: (value: 'nvm' | 'nvs' | 'none') => void; + setNodeVersionManagerFile: (value: '.nvmrc' | '.node-version') => void; } export const AdditionalStep: React.FunctionComponent = ({ @@ -23,7 +29,64 @@ export const AdditionalStep: React.FunctionComponent = ({ shouldInstallReusableReactControls, setShouldInstallReusableReactControls, shouldInstallPnPJs, - setShouldInstallPnPJs }: React.PropsWithChildren) => { + setShouldInstallPnPJs, + shouldCreateNodeVersionFile, + setShouldCreateNodeVersionFile, + setNodeVersionManagerFile, + nodeVersionManager, + setNodeVersionManager +}: React.PropsWithChildren) => { + // Send a message to retrieve the default value for the create node version file + const getCreateNodeVersionFileDefaultValue = React.useCallback(() => { + Messenger.send(WebviewCommand.toVSCode.createNodeVersionFileDefaultValue, {}); + }, []); + + // Send a message to retrieve the node version manager file + const getNodeVersionManagerFile = React.useCallback(() => { + Messenger.send(WebviewCommand.toVSCode.nodeVersionManagerFile, {}); + }, []); + + // Send a message to retrieve the node version manager + const getNodeVersionManager = React.useCallback(() => { + Messenger.send(WebviewCommand.toVSCode.nodeVersionManager, {}); + }, []); + + // Listen for the response to the message/s + React.useEffect(() => { + const messageListener = (event: MessageEvent) => { + const { command, payload } = event.data; + switch (command) { + case WebviewCommand.toWebview.nodeVersionManager: + setNodeVersionManager(payload); + break; + case WebviewCommand.toWebview.nodeVersionManagerFile: + setNodeVersionManagerFile(payload); + break; + case WebviewCommand.toWebview.createNodeVersionFileDefaultValue: + setShouldCreateNodeVersionFile(payload); + break; + default: + break; + } + }; + + Messenger.listen(messageListener); + + return () => { + Messenger.unlisten(messageListener); + }; + }, [setNodeVersionManager, setNodeVersionManagerFile, setShouldCreateNodeVersionFile]); + + // Sends the requests to load the settings values only once + React.useEffect(() => { + // Get the default value for the create node version file + getCreateNodeVersionFileDefaultValue(); + // Get the node version manager + getNodeVersionManager(); + // Get the node version manager file + getNodeVersionManagerFile(); + }, []); + return (
@@ -50,6 +113,12 @@ export const AdditionalStep: React.FunctionComponent = ({ setValue={setShouldInstallPnPJs} label='Install PnPjs (@pnp/sp, @pnp/graph)' link='https://pnp.github.io/pnpjs/' /> + + {nodeVersionManager !== 'none' && + }
); diff --git a/src/webview/view/components/forms/spfxProject/ScaffoldSpfxProjectView.tsx b/src/webview/view/components/forms/spfxProject/ScaffoldSpfxProjectView.tsx index ea7df72..3d52640 100644 --- a/src/webview/view/components/forms/spfxProject/ScaffoldSpfxProjectView.tsx +++ b/src/webview/view/components/forms/spfxProject/ScaffoldSpfxProjectView.tsx @@ -26,6 +26,9 @@ export const ScaffoldSpfxProjectView: React.FunctionComponent(false); const [shouldInstallReusableReactControls, setShouldInstallReusableReactControls] = useState(false); const [shouldInstallPnPJs, setShouldInstallPnPJs] = useState(false); + const [shouldCreateNodeVersionFile, setShouldCreateNodeVersionFile] = useState(false); + const [nodeVersionManager, setNodeVersionManager] = useState<'nvm' | 'nvs' | 'none'>('nvm'); + const [nodeVersionManagerFile, setNodeVersionManagerFile] = useState<'.nvmrc' | '.node-version'>('.nvmrc'); const location: any = useLocation(); useEffect(() => { @@ -78,7 +81,10 @@ export const ScaffoldSpfxProjectView: React.FunctionComponent }