Code to NPM Packages

a simple command prompt.

Introduction

A how-to guide about the philosophy, design, repositories, building, and deployment of component code Modules, Command Line Interface (CLI), or Cloud Functions (like AWS Lambda) via npm packages.

During my exploration into the Nuxt Static S3 issues and Nuxt Static S3 fixes, I identified custom CICD requirements to implement the fix I had identified.

  1. Create a list of paths from a sitemap.xml file
  2. Compare the list of paths of a new build sitemap.xml
  3. Perform custom AWS S3 commands based on the differences between the prod and new build.

I knew that each component of functionality should be modular and could be a useful in other contexts and code bases as a node module (NPM) package. Further we might want to create a regular NPM module, a standalone Command-line Interface (CLI), or even a cloud function.

  • sitemapParse
  • sitemapCompare
  • updateS3NuxtSSG

Node Package Manager

I have always relied on Node Package Manager (NPM) and its registry of modular packages at npmjs.com. And even though my startup was an early sponsor of NPM, I had only ever contributed code to private package manager like JFrog Artifactory for my employers. It's time to contribute some open source code to the public via NPMjs.org.

NPM Best Practices

How should my code be organized, tested, for a good NPM package that others might find useful?

  • Use a package manager like rollup.js
  • Use TypeScript so you can share types
  • package.json - main npm package configuration.
  • tsconfig.json - TypeScript configuration.
  • rollup.config.js- package manager rollup.js config
  • Support both ESM (default for NPM) and CJS (CommonJs) for use in the client and node servers.
  • use git branch protection rules and use PR process to protect main branch

Configuration

config filekeyvaluesystem
package.jsontypemoduleESM
package.jsontypecommonjsCJS
tsconfig.jsonmodulees6ESM
tsconfig.jsonmodulecommonjsCJS

see NPM Cheat Sheet

Module Systems

JavaScript Module Systems

  1. CJS - CommonJS
  • AMD - Asynchronous Module Definition
  • UMD - Universal Module Definition = CJS + AMD
  1. ESM - ECMA Script Modules

ESM is modern and can use CJS modules. CJS is older and can't use ESM modules1

ESM key wordsCJS key words
import--------->require
export<---XX---module exports

Support ESM and CJS for greatest compatibility for client, server, and operation code bases.

TypeScript

Typescript is recommended for NPM package due to its support for typing and assisting in documentation and usage.

TypeScript options necessary - compilerOptions.declaration - true/false, omit declaration files while builds - compilerOptions.declarationDir - path, where declaration files will be - compilerOptions.target es6 - trans pile code to ECMA script 6 - compilerOptions.moduleResultation - use "node" - include: - array of source code directories, glob patter src/**/* - exclude: - ignore the array of directories for source code

Rollup Bundler

RollupJs is a package bundler which will place all key files and build artifacts into a bundle for distribution. For greatest compatibility we will support both module types

  1. ESM Bundle
  2. CJS Bundle

Update package.json to to allow for rollup bundling

package.json

{
  // ...
  "name": "<npm_user_name>/<npm-package-name>",
  "type": "module",
  "main": "lib/index.cjs",
  "exports": {
    "import": {
      "default": "./lib/index.esm.js",
      "types": "./lib/types/index.d.ts"
    },
    "require": {
      "default": "./lib/index.cjs",
      "types": "./lib/types/index.d.ts"
    }
  },
  "files": [
    "lib"
  ],
  "scripts": {
    "build": "rollup -c",
    "release-package": "npm run build && npx changeset publish"
  }
  // ...
}

Changesets Automatic Publishing

  • Changesets is an automatic package versioning CLI and GitHub Action toolset.

References

Footnotes

  1. CJS can now load ESM. This is no longer true for newer Node versions with some restrictions. Version 23 of Node already have support for loading ES modules using require() under certain conditions (the main condition currently is that it only supports loading synchronous ES modules). This support was also added to v22.0.0 and v20.17.0. You can read more about it in the official docs