Npm scopes

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019 | TFS 2018

Npm scopes are a way of grouping related packages together. A scope allows you to create a package with the same name as other packages created by different users without conflicts. Using scopes, you can separate public and private packages by adding the scope prefix @SCOPE_NAME and configuring the .npmrc file to only use a feed for that scope. With Azure Artifacts, you can publish and download both scoped and non-scoped packages to/from feeds or public registries. Using npm scopes is also useful with self-hosted on-premise servers that do not have internet access because setting up upstream sources in that case is not possible. Using scopes:

  • We don't have to worry about name collisions.
  • No need to change the npm registry in order to install or publish our packages.
  • Each npm organization/user has their own scope, and only the owner or the scope members can publish packages to their scope.

Project setup

  1. Select Artifacts, and then select Connect to feed.

    A screenshot showing how to connect to a feed.

  2. Select npm, and then select Other.

  3. Add a .npmrc file in the same directory as your package.json, and paste the following snippet into your file.

    registry=https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/registry/
    
    always-auth=true
    

Set up credentials

  1. Copy the following snippet into your .npmrc file.

    • Organization-scoped feed:

      ; begin auth token
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/registry/:username=[ENTER_ANY_VALUE_BUT_NOT_AN_EMPTY_STRING]
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/registry/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/registry/:email=npm requires email to be set but doesn't use the value
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/:username=[ANY_VALUE_BUT_NOT_AN_EMPTY_STRING]
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/npm/:email=npm requires email to be set but doesn't use the value
      ; end auth token
      
    • Project-scoped feed:

      ; begin auth token
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/registry/:username=[ENTER_ANY_VALUE_BUT_NOT_AN_EMPTY_STRING]
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/registry/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/registry/:email=npm requires email to be set but doesn't use the value
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/:username=[ENTER_ANY_VALUE_BUT_NOT_AN_EMPTY_STRING]
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
      //pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/npm/:email=npm requires email to be set but doesn't use the value
      ; end auth token
      
  2. Generate a personal access token with Packaging > Read & write scopes.

  3. Run the following command to encode your newly generated personal access token. Paste your personal access token when prompted.

    node -e "require('readline') .createInterface({input:process.stdin,output:process.stdout,historySize:0}) .question('PAT> ',p => { b64=Buffer.from(p.trim()).toString('base64');console.log(b64);process.exit(); })"
    
  4. Open your .npmrc file and replace the placeholder [BASE64_ENCODED_PERSONAL_ACCESS_TOKEN] with your encoded personal access token you just created.

  1. Select Packages, and then select Connect to feed.

  2. Select npm.

  3. Select Generate npm credentials, and then copy the credentials and add them to your .npmrc file.

    Screenshot showing how to generate npm credentials in TFS.

In your .npmrc file, replace registry=<YOUR_SOURCE_URL> with @SCOPE_NAME:registry=<YOUR_SOURCE_URL>. Make sure you add the scope and package names to your package.json file: { "name": "@SCOPE_NAME/PACKAGE_NAME" }.

@[SCOPE_NAME]:registry=https://pkgs.dev.azure.com/[ORGANIZATION_NAME]/_packaging/[FEED_NAME]/npm/registry/
    
always-auth=true
{
"name": "[@SCOPE_NAME]/[PACKAGE_NAME]" 
}

Upstream sources vs scopes

Upstream sources give you the most flexibility to use a combination of scoped and non-scoped packages in your feed, as well as scoped and non-scoped packages from public registries such as npmjs.com.

Scopes add another restriction when naming your packages: each package name must start with @<scope>. If you want to publish your private packages to public registries, you must do so with the scopes intact. If you remove package scopes when deploying your packages, you'll need to update all the references in your package.json. With that in mind, scopes can be a viable alternative to upstream sources.