Article
0 comment

Use SharePoint Framework with npm / yarn work spaces

… another title for this blog post could be: “How to use SharePoint Framework and share code between projects without Microsoft’s SharePoint Framework Library component or NPM registry.

On top of that, Andrew Connell’s “Sharing Code in SharePoint Framework (SPFx) Projects: npm vs Library Components” recently caught my attention too. This is a great article, but there are easy ways to overcome the disadvantages of the npm packages.

With that all settled, let me explain how to do it.

In June 2021, NPM introduced a new concept called npm workspaces. The simple benefit is using multiple projects in a so-called mono repo. Using such a workspace lets you have numerous projects and gives you better control, especially regarding dependency handling.

For SharePoint Framework, this means you can have, for example, all your customer’s projects in one single repository and treat them as if they were individual ones.

What do npm workspaces mean for SharePoint Framework?

Let’s examine the project setup. You can find multiple SPFx projects in the project’s root folder.

.
├── package.json. # Controls the workspace
└── node_modules  # Shared dependencies of SPFx Project 1 + 2
└── spfx-project-1
        └── package.json # Dependencies of SPFx Project 1
└── spfx-project-2
    └── package.json # Dependencies of SPFx Project 2
└── library-component
    └── package.json # Dependencies library component

One package.json in the root folder defines where the workspace projects are located, and, for example, based on the previous configuration, it looks like this.

{
  "name": "all-Customer-1-projects",
  "workspaces": [
    "spfx-project-1",
    "spfx-project-2",
    "library-component"
  ],
  "scripts": {
    "spfx1:dev": "gulp bundle -f ./spfx-project-1/gulpfile.js --workspace spfx-project-1",
    "spfx1:bndl": "gulp bundle -f ./spfx-project-1/gulpfile.js --workspace spfx-project-1",
    "spfx2:dev": "gulp serve  -f ./spfx-project-2/gulpfile.js --workspace spfx-project-2",
    "spfx2:bndl": "gulp bundle -f ./spfx-project-2/gulpfile.js --workspace spfx-project-2",
    "lib-comp": "npm run start --workspace library -component"
  }
}

The entries under “Workspaces” define all folders that belong to the project. In addition, you can define all the scripts to run individually at the root level of the workspace.

One file to rule them all.

Install library component to SPFx projects

Now that everything is combined in the workspace configuration, you can install each workspace as a dependency of another.

# npm install <workspace-pkg> --workspace=<selected-workspace-for-dependency>
> npm install library-component --workspace=spfx-project-1

This command installs the library component to ‘spfx-project-1’. The result is the modified dependencies of SPFx-Project-1.

{
  "dependencies": {
    // library-component
    "library-component": "^1.0.0",
    "tslib": "2.3.1"
  }
}

library-component not in same GitHub Repo

There is no requirement to have the code directly in the same repo. The key to accomplishing this is to use git submodules.

What git submodules do is bring in the code from another repo without duplicating the code.

For example, Elio Struyf shows how to use submodules as a theme in a Hugo Site.

Another great introduction can be found on YouTube in “Git Submodules Tutorial | For beginners”.

More information also can be found on the Github Blog.

Submodules come with a small benefit in addition to this. Git stores the commit information, which code to check out from the remote repository, so you know exactly how the code looked when you last updated the submodule.

A small issue with TypeScript, RushStack or SPFx

I’m not quite sure who is responsible for this small issue, but I think it is caused by how the rushstack compilers work.

Error when running SPFx in a workspace

You might run into the issue “error TS5083: Cannot read file ‘“ caused by the typescript config file. Since all node_modules are located in the workspace’s root, the ’tsconfig.json’ has trouble finding the other configuration files.

In the TypeScript configuration, you will find the following entries.

{
  "extends": "./node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json",
  //...
}

Change the code above to this because the node_modules are one level higher.

{
  "extends": "../node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json",
  //...
}

Under “compilerOptions”, add the following entry.

{
  //...
  "compilerOptions": {
    //..
    "rootDir": ".."
    //..
  }
}

TypeScript cannot run quote-on-quote outside the particular folder, so we must tell the compiler that the root directory is one level higher.

Lastly, the type roots are also located one folder level higher and need to be changed to:

  "typeRoots": [
    "../node_modules/@types",
    "../node_modules/@microsoft"
  ],

After those changes, in our case, for both SPFx projects, nothing is in the way of using an npm library without publishing it to an npm registry or using SharePoint Framework library components.

Last words

The method described here is common. It’s more. Many full-stack developers outside of the SharePoint and Microsoft ecosystems are aware of and use it daily.

We have used this npm workspace approach with huge success for quite some time now because it makes things so much easier in many ways. It doesn’t limit your event to publish the library component to a CND or an npm package. If you like to get access to this sample project please subscribe to my monthly newsletter and you will get a link to download.

Julie Turner and I have a special session at the European Collaboration Summit in May about this topic and how we combine a design system and bring it as a dependency into SFPx. The session is named Design + Development: A Case Study. I hope to see you there.

PS: You might have missed my old blog post on “Tips and Tricks working with SPFx library components”. SharePoint Framework library components were an option but not a good one.

Leave a Reply

Required fields are marked *.


This site uses Akismet to reduce spam. Learn how your comment data is processed.