This is a topic which has been discussed quite a lot since the launch of the SharePoint Framework. The SPFx Yeoman generator is recommended to be installed globally, but what if a new version is released and I want to try out the new version without uninstalling my globally installed generator?
Waldek Mastykarz has a great post on the issue and suggests some solutions as well. Have a look at his post if you haven't already: Why you should consider installing the SharePoint Framework Yeoman generator locally
In this post, lets have a look at how we can use npx to provide a solution to this problem.
If you haven't heard about npx yet, it is the hot new member of the node ecosystem. Check out the introduction post by Kat Marchán:
In short, npx can be used to run npm packages directly from the command line without having to install them globally or locally.
npx comes bundled with firstname.lastname@example.org but unfortunately, the SharePoint Framework does not support email@example.com yet.
Lucky for us, npx comes as a standalone npm package as well: https://www.npmjs.com/package/npx
We are going to use the npx package with firstname.lastname@example.org
From the intro post:
Calling npx <command> when <command> isn’t already in your $PATH will automatically install a package with that name from the npm registry for you, and invoke it. When it’s done, the installed package won’t be anywhere in your globals, so you won’t have to worry about pollution in the long-term.
This means that when we run the SharePoint Framework generator through npx, it will be as if it is running from a global install. After the SPFx solution is created, you will not find the generator installed either locally or globally. It has served it's purpose of generating us a solution and is no longer needed.
Run different versions of the SPFx Yeoman generator in different folders:
Now lets actually use npx to create two different SPFx solutions using two different versions of the SharePoint Framework Yeoman generator. We will use the latest version of the generator and the very first version of the generator which was part of SPFx Drop 1.
I am using the following command throughout the post to list the top level global packages:
I have done a global install for gulp already. This is to keep things simple as we only want to use npx for the SharePoint Framework generator. If we wanted to take things to the next level, we could use gulp through npx as well.
Now, let's go ahead and install npx globally:
After it's installed successfully, lets have a look at our global packages again:
As you can see, neither the SharePoint Framework generator, nor Yeoman itself is installed globally. This makes us free to use any version of the generator we want. Let's use the latest version, which at the time of this writing is 1.1.0
First, I am going to create a new folder named "latest" and navigate to it. Then run the following:
By specifying the -p (package) flag we run the yeoman (yo) and SPFx generator (@microsoft/generator-sharepoint) packages through npx.
After that, we run the SharePoint Framework generator by running -- yo @microsoft/sharepoint
Now let's run gulp serve to launch the workbench. As mentioned before, since we have already installed gulp globally, we don't have to install it again.
Now let's see the real power of npx. We will navigate to another folder and use Drop1 of the SharePoint Framework generator to create an SPFx solution.
I have created another folder called drop1. Lets navigate to it and run the following using npx:
As you have probably noticed already, the only difference in this and the previous npx command is that we are using the version numbered 0.0.65 of the generator and not the latest. This was the version of the generator when Drop 1 of the SharePoint Framework landed back in August 2016.
You will notice immediately that there are some differences in the wizard. It asks me whether to create a new folder for the solution or use the current folder. It also defaults to creating a web part and does not ask me whether I want to create an extension. This is obvious given that extensions were not part of the first drop.
After running gulp serve, we can see the Drop 1 workbench running:
That's it! We have used two different versions of the generator to create two different solutions without having to install the generator on our machine!
Using a new version of the SPFx generator when an older version is already installed globally:
If you have been closely following the post, you know what's coming next.
Let's say we already have a version of the generator installed globally. Now a newer version of the SPFx generator is released and we want to try it out immediately. All we have to do is use npx to run the latest version. We would be able to create a new SPFx solution with the latest generator and still keep our old generator installed globally!
The magic is described in the npx package description:
If a full specifier is included, or if --package is used, npx will always use a freshly-installed, temporary version of the package. This can also be forced with the --ignore-existing flag.
This is good news because we want to have control over which generator version is used by npx to create our solution.
Here is how the process will look:
I already have the Drop 1 (0.0.65) version of the generator installed globally. Next, we are running npx and specifying it to use the latest version of the generator.
After the command is run, we are presented with the wizard which gives us the option of creation an SPFx extension. This option was not available with Drop 1 which means that the latest generator is being used to create our solution.
This is how we can use npx to explicitly specify the generator version and ignore the globally installed generator!
Where does npx store the packages?
If we navigate to our npm-cache folder, we will see that npx stores all the versions of the package in the cache. Here, we see that the @microsoft/sp-build-web package was used in both versions of the generator, hence both versions are being stored in the cache:
That's it! Hope you have enjoyed this post as much as I have enjoyed writing it :)