Thursday, July 27, 2017

How to avoid breaking your SPFx project when dependencies change

Your SPFx project has a short list of dependencies.
They are listed in package.json

Thing is, these dependencies have a looong list of dependencies themselves, so that you end up with over 300MB folder of node_modules by the time you finish running npm install on a new SPFx project.

As most of you probably noticed, SPFx by default has a .gitignore file that excludes node_modules from ever going into your source control.

Which is a good thing - these packages are monitored and controlled by their authors and you don't need to manage all that code in your project, do you?

That means, whenever you move to a new computer or a new developer start working on a project, after cloning that project they must also run npm install before they can start working.

Now, npm install will start loading packages and dependencies as of TODAY, not as of when the project or the SPFx framework were compiled.

Some dependencies are specified by a version number, so no worries there (as long as the author did not re-publish new code with the same version number).

Others are specified as latest, or "*" or just with a major version - allowing minor versions updates. Since minor version updates suppose to be fully backward compatible and not contain breaking changes - sometimes that is not the case.

So, you go to your new PC, run npm install, and your project does not build.
You get a ton of errors, version mismatch, and have no clue why.

The reason would be probably a dependency you had was updated during that time (or god forbid - removed from npm!) which now breaks your project.

That is something I talked about during the first DevKitchen I attended, when I first saw SPFx. Having a huge list of dependencies I don't control, my project rely on, and that I don't have a copy of scared me.

And sure enough, since that day until today I had at least 4 episodes of dependency hell.

What can you do today

Without going into what Microsoft can and should do to avoid this in the future, here is what I plan to do and encourage you guys to do as well:

1. Build a test SPFx project, call it "Sanity"
2. Run npm install, and make sure the demo web part works by running gulp serve
3. Remove the .gitignore file
4. Store the entire huge project in your source control

Every so often, run npm update on this project.
Test if it is working - and if yes, commit the changes.

However, if your project fails to build, you know you got a breaking change. Do not commit, it will probably get fixed within a few hours.

Note that some breaking changes may come from a globally installed package, such as typescript.
So, it is a good practice to log these versions as well somewhere in your project. You can get that list by running "npm ls".

What to do if your project breaks

Now, once one of your project fails to build, look at where the errors are coming from.
It will probably tell you the packages and their dependencies.
Now, you can compare these packages with the ones you have in your "Sanity" project, and once you identify the ones causing the problem you can manually revert it by running:
npm uninstall {package}
npm install {package}@{last working version}

If you don't have that "Sanity" package, and do have a working version on a different computer - try to compare the versions between these two.

A quicker fix might be to simply copy the node_modules folder from the working project into your new broken projects.

It could be very frustrating spending valuable dev hours on maintenance instead of productive work, but hopefully these steps could help you cut down those hours.

SPFx project breaking after moving to a new computer

So, as I was finishing my demo for #SPSNYC about SPFx, I moved my demo from my PC to my Mac laptop.

Running npm install everywhere, it all goes smooth, except I didn't have TypeScript installed.

So, naturally - I installed TypeScript:
npm install -g typescript

Next, I went to verify all demos were working.

Most were, one project refused to build.

Trying to build it yielded an error:

xxx:sharedcode xxx$ tsc
node_modules/@microsoft/sp-http/lib/httpClient/HttpClientResponse.d.ts(15,22): error TS2420: Class 'HttpClientResponse' incorrectly implements interface 'Response'.
  Types of property 'type' are incompatible.
    Type 'string' is not assignable to type 'ResponseType'.
node_modules/@types/react/index.d.ts(165,11): error TS2559: Type 'Component
' has no properties in common with type 'ComponentLifecycle
'.

Wow.

That probably means, one or more packages have changed since the time I created this project to the time I built it.

Luckily, finding out where the error comes from was rather simple.

See, the error complained an SPFx package was incorrectly implementing an interface.
That means, it is either missing a member, or have the wrong type of a member.
It even gave me the member name: Type, which was set to string instead of ResponseType.

So, I opened the HttpClientResponse.d.ts that reported the error, and noticed its "Type" member was of type "String".

Right click on the "Responce" interface, go to definition - indeed showed that Type was defined as "ResponseType".

I knew that my SPFx dependencies version didn't change, so I quickly checked the TypeScript version using "tsc -v"

On my laptop, it was 2.4.2
On my PC it was 2.1.5

So, the fix was simple:
1. npm uninstall -g typescript
2. npm install -g typescript@2.1.5

And viola! Everything back to normal.

Working with SPFx, you should know this is not the first time something like that happened, and unless steps are taken - this won't be the last either.

I will talk about this dependencies "hell" and what can be done about it during my session at #SPSNYC, so come see me if you can!

Monday, July 24, 2017

SPFx: Sharing Modules between projects

This is something that is not covered all that much in SPFx talks I've been to, and is definitely something everyone should know how to do.

Imagine you have several SPFx projects, and they all need to connect to the same LOB system. Or maybe you are an ISV that needs to check for product licenses. Perhaps you have your own UI library that your company built and you need to use, or even just a few helper functions that you always wished were a part of the JavaScript language and hate that you have to re-write them every time.

In all these cases, you will end up with a bunch of code that you wish you could put in once place and reuse in several SPFx projects.

For the .Net developers out there - that would probably go into a DLL that you would reference from your other projects. Right?

But, how do you do it in SPFx?

Well, turns out there are MANY ways to do it, they are different, and not all of them would lead to a good practice or comfortable code management.

I won't list them all, of course, but will show you what I chose to do and share some of the considerations behind it.

Relative folder

I guess the easiest way to deal with it is to just place your code in a separate folder.
You will create a folder with your module name, say "shared-code".
Now, when you want to use it from different projects - all you got to do is import it from the relative folder path, for example:
import Utilities from '../../../../shared-code/Utilities';
Now, say you want to use TypeScript in your shared module. You will need to create a tsconfig.json file and complile the TS into JS files before you can use them.
Example of tsconfig.json I use:
{
  "compilerOptions": {
    "target": "es5",
    "forceConsistentCasingInFileNames": true,
    "module": "amd",
    "moduleResolution": "node",
    "declaration": true,
    "sourceMap": true,
    "experimentalDecorators": true,
    "types": [
      "es6-promise",
      "es6-collections"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

* notice I use module "amd". This is important if you later plan to load it as external from CDN and don't want it to register a global object.


And if you are using TypeScript, you probably would want to import some SPFx types and objects to work with, right? Since we don't have a package, you will have to manually add the npm packages for these using these commands:
    npm install @microsoft/sp-core-library@~1.1.0

    npm install @microsoft/sp-webpart-base@~1.1.1


Now, when your code is ready, simply run "tsc" in that folder to compile it to JavaScript and your are ready to go.

Advantages

Quick and easy. You can automate the tsc compiler task so it will be completely transparent to you.

Disadvantages


  1. Since we don't have a package, there is no way to trace versions.
  2. You cannot set package dependencies and restore them automatically with npm install later on.
  3. Also, you cannot mark it as external and load it from a CDN. It must be bundled with your web parts.

Separate Package

If you want to be able to use your shared module as a package, you will need to create a package definition file.
Luckily, NPM created a helper that does it for you.
Go back to your "shared-code" folder, and run "npm init" command. Follow the instructions to create a package definition file. While running npm init, a couple of notes to keep in mind:

  • Only lower case and - are supported.
  • Author needs to be in this format: name <email> (web site)
  • To specify a license file, set it to: "SEE LICENSE IN <filename>"
Once this is done you will have a new package.json file created in your folder. Open it.
You will see your have a version number here. Every time you make changes to your code, you should update the version number. This is whats telling "npm update" command when to update your package.
Also, if your are using TypeScript, you should add "typings" to your package definition file.
Now, we can add the 2 SPFx node modules as proper dependencies instead of installing them manually. Add "dependencies" to the end of your package.json file with the 2 packages. Your file should look like this:
{
  "name": "shared-code",
  "version": "1.0.0",
  "description": "my shared code",
  "main": "utilities.js",
  "typings": "utilities.d.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "KWizCom  (http://www.kwizcom.com)",
  "license": "SEE LICENSE IN license.md",
  "dependencies": {
    "@microsoft/sp-core-library": "~1.1.0",
    "@microsoft/sp-webpart-base": "~1.1.1"
  }
}

Once you are done, save the file. You can now install dependencies using "npm install", and get updates using "npm update".



* You might also want to create a license file if you specified one during the npm init.

Last, you need to publish your package. Either you commit to GitHub or publish to npm using "npm publish" - you will be able to reference your package by name from that source.

When you want to use it in your SPFx projects, you can add it as a dependency to your package.json file:
"dependencies": {
  "@microsoft/sp-core-library": "~1.1.0",
  "@microsoft/sp-webpart-base": "~1.1.1",
  ...
  "shared-code": "*"
}

Advantages


  1. Having a package helps keep track on dependencies inside your package.
  2. A package file keeps track of versioning.
  3. When using a package, it is added as a dependency just like any other library you are using.
  4. You can mark packages as external in config/config.json file, to load it from a CDN instead of bundling it into your JS file:
"externals": {
  "shared-code": "https://apps.kwizcom.com/shared-code/utilities.js"
},

Disadvantages

You have to publish this package to a different project/source (npm or GitHub or both).
This makes tracking and managing your code a bit more complex, if you are not already using that system for your source control.

Separate local package

Much like the published npm package, you can follow all steps except - don't publish it to npm/GitHub separatly.
I keep all my projects including the shared packages in the same source control system, in the same folder structure.
When I set up the dependencies for my packages, since I store them all in the same source control and they will be available locally together - I can specify a local folder relative path to that package.
In the package.json file, I set it up as:
   "shared-code": "file:../shared-code"

Advantages

I find this to be the easiest way in my environment, since I don't have to keep track of two different systems or projects. When I sync changes from the server, I get updates for all projects and for my shared module at the same time.

Disadvantages

In my case, that works best. But if your projects or source control system does not set up to keep the folder structure and get updates from all projects at the same time - you might want to get your package from npm or GitHub. That's because if you ever get a new PC and run "npm install" it might not be able to find and get this shared package from a local folder if you didn't get it previously.

Type mismatch

There is one issue you might notice when working with TypeScript.
You might get an error reporting a type mismatch between the same object. For example, when you send your web part context into a function in shared-code, it might say the objects do not match.
This is caused because the SPFx libraries are not installed globally, so each project has its own definition of these objects. A simple solution is to send it as type any, I will discuss a better resolution for this conflict in a later post.

Closing

I know this sounds complicated, and honestly it is much more complicated than it should be. In C#, building a dll library was a simple common task.
But, like everything else, once you get used to it - it is not that bad.
(If you enjoy typing console commands and editing json files...)

Hope this helped you make sense of the procedure better, I didn't find a simple clear instructions on this topic yet so I thought it is worth sharing.

Tuesday, July 18, 2017

Debugging SPFx using VSCode - multiple projects in sub folders

I've been quite happy with Chrome TS debugging capabilities, honestly, so I never really cared to try VSCode debugging until now.
Thing is, I've been working on my "SPFx from an ISV point of view" session and wanted to cover all options in my presentation, so I thought I should probably set it up and write about it.
Finding the instructions was simple enough, and the guys @msft did a good job detailing how its done here.
However, in my situation (for reasons I will share later on) we have decided to create a root SPFx folder and place our projects as sub folders under that root folder. So, when I open my VSCode, the root folder is not my SPFx project root.
That folder structure broke the debugging configuration file and it couldn't find any maps files.
I could not find any documentation on the different properties in the JSON config file, but it was simple enough to understand.
After playing around with it for a bit, I figured the debugger used the "webRoot" property to find the source files inside VSCode.
All I needed to do is change:
"webRoot": "${workspaceRoot}"
to:
"webRoot": "${workspaceRoot}/DVP"
and my debugger connected correctly and started working.
Why do I use sub folders? That - I will explain in a later post, or in my talk at the end of the month @SPSNYC.