Metalsmith Redux: Dependencies and Scripts

In our previous post, we explored the file structure of our Metalsmith2025 Simple Starter. Today, we'll examine package.json, one of the most important files in any Node.js project.

While it might not be the most exciting file in our project, package.json is crucial. It defines our project, what it depends on, and the commands we can run to build, develop, and maintain it.

Let's dig in and understand what each part of this file does.

What is package.json?

Think of package.json as your project's ID card and instruction manual combined. It tells Node.js and npm (Node Package Manager) everything they need to know about your project: its name, version, dependencies, scripts, and more.

Every Node.js project, including our Metalsmith site, needs a package.json file. Without it, none of the tools we're using would work.

The Basic Metadata

At the top of our package.json file, we see the basic information about our project:

{
  "name": "metalsmith-blog-starter",
  "version": "1.0.0",
  "description": "A simple Metalsmith blog starter",
  "type": "module",
  "keywords": [
    "metalsmith",
    "starter"
  ],
  "author": "werner@glinka.co",
  "license": "MIT",
}

Let's break down what each of these fields means:

  • name: The name of our project. This is how other developers would refer to it if we published it as a package.
  • version: The current version of our project, following semantic versioning (MAJOR.MINOR.PATCH).
  • description: A brief explanation of what the project does.
  • type: Set to "module", which means we're using ECMAScript modules (import/export) rather than CommonJS (require/module.exports).
  • keywords: Tags that help people find our project if we publish it.
  • author: The creator of the project.
  • license: The legal terms under which the project is distributed. MIT is a permissive open-source license.

These fields aren't strictly necessary for our project to work, but they provide essential context for anyone who looks at our code.

The engines Field

Near the bottom of the file, you'll find:

"engines": {
  "node": ">=18.0.0"
}

This tells us that our project requires Node.js version 18.0.0 or higher. This is important because some features we're using might not be available in older versions of Node.js.

The Scripts: Commands to Run

The scripts section defines commands that we can run in the terminal with npm run [script-name]:

  "scripts": {
    "dev": "metalsmith -c metalsmith.js --env NODE_ENV=development --env DEBUG=@metalsmith*",
    "build": "metalsmith -c metalsmith.js --env NODE_ENV=production",
    "start": "NODE_ENV=development DEBUG=@metalsmith* node metalsmith.js --watch",
    "serve": "browser-sync start --server 'build'",
    "format": "prettier --write \"**/*.{js,json,njk,css}\"",
    "lint": "eslint --fix .",
    "fix": "npm run format && npm run lint",
    "depcheck": "depcheck"
  }

These scripts are the commands you'll use most often when working with the project. Let's go through each one:

Building and Development Scripts

dev: Builds the site in development mode with debug information enabled. This is what you'll use most often during development. Each plugin used in the build process will log debug information and show you what's happening under the hood.

metalsmith -c metalsmith.js --env NODE_ENV=development --env DEBUG=@metalsmith*

This command tells Metalsmith to:

  • Use metalsmith.js as its configuration file (-c metalsmith.js)
  • Set the environment to development (--env NODE_ENV=development)
  • Enable debug logging for Metalsmith plugins (--env DEBUG=@metalsmith*)

build: Builds the site in production mode, which might include additional optimizations and exclude development features.

metalsmith -c metalsmith.js --env NODE_ENV=production

start: An alternative development mode that directly runs metalsmith.js as a Node.js script with file watching enabled. So when you make changes to your source files, they will be automatically shown in your browser.

NODE_ENV=development DEBUG=@metalsmith* node metalsmith.js --watch

This is my preferred way to work with Metalsmith when I build a site. The terminal output is not overwhelmed with plugin debug information so I can focus on relavent content or metadata logging. We'll explore this in more detail in the next post.

serve: Starts a local development server that serves the files from the build directory.

browser-sync start --server 'build'

Code Quality and Maintenance Scripts

format: Uses Prettier to automatically format your JavaScript, JSON, Nunjucks, and CSS files.

prettier --write "**/*.{js,json,njk,css}"

This ensures consistent code style across the project.

lint: Uses ESLint to check your JavaScript files for potential errors and automatically fixes them when possible.

eslint --fix .

fix: Runs both format and lint commands in sequence.

npm run format && npm run lint

This is a convenient way to clean up your code in one go.

depcheck: Checks for unused dependencies or dependencies missing from package.json. This is really helpful when you're managing a project with many dependencies. Dependencies have a tendency to accumulate over time... at least in my case.

depcheck

Development vs. Production Mode

You might have noticed that several scripts specify either development or production mode. This typical pattern in Node.js applications enables different behavior in different environments.

  • Development mode includes drafts, detailed debug information, and watches for file changes
  • Production mode excludes drafts, minifies HTML, generates a sitemap, and performs other optimizations

This distinction ensures that we have a smooth development experience while still producing optimized output for deployment.

The Dependencies: What Our Project Needs

The dependencies and devDependencies sections list all the packages our project relies on:

"devDependencies": {
  "browser-sync": "^3.0.4",
  "eslint": "^9.24.0",
  "prettier": "^3.5.3"
},
"dependencies": {
  "@metalsmith/drafts": "^1.3.0",
  "@metalsmith/layouts": "^3.0.0",
  "@metalsmith/permalinks": "^3.2.0",
  "depcheck": "^1.4.7",
  "jstransformer-nunjucks": "^1.2.0",
  "marked": "^15.0.8",
  "metalsmith": "^2.6.3",
  "metalsmith-blog-lists": "^2.0.2",
  "metalsmith-menu-plus": "^0.0.3",
  "metalsmith-optimize-html": "^0.5.3",
  "metalsmith-simple-pagination": "^0.0.1",
  "metalsmith-sitemap": "^1.2.2",
  "metalsmith-static-files": "^1.1.1",
  "metalsmith-unified-markdown": "^0.0.4",
  "rehype-highlight": "^7.0.2"
}

There's a distinction between the two types of dependencies:

  • dependencies: Packages required for the project to run in production
  • devDependencies: Packages only needed during development, like testing tools or development servers

Let's break down what each package does, starting with the dev dependencies:

DevDependencies

  • browser-sync: Creates a local development server with automatic browser reloading
  • eslint: Checks your JavaScript code for potential problems
  • prettier: Automatically formats your code for consistency

Dependencies

Core Metalsmith and Templating
  • metalsmith: The core static site generator that powers our project
  • jstransformer-nunjucks: Enables Nunjucks templating in our layouts
Metalsmith Core Plugins (Official)

@metalsmith/drafts: Excludes draft content in production builds @metalsmith/layouts: Applies templates to content files @metalsmith/permalinks: Creates clean URLs without file extensions

Community Metalsmith Plugins
  • metalsmith-blog-lists: Generates lists of blog posts (latest, featured, etc.) and places them in the Metalsmith metadata. These lists can then be used in sidebars or footers.
  • metalsmith-menu-plus: Creates navigation menus and breadcrumbs from your content
  • metalsmith-optimize-html: Minifies HTML in production builds
  • metalsmith-simple-pagination: A simple pagination plugin for folder content
  • metalsmith-sitemap: Generates a sitemap.xml file for search engines
  • metalsmith-static-files: Copies static files (CSS, images, etc.) to the build directory
  • metalsmith-unified-markdown: Processes Markdown content with unified/remark/rehype. Uses retype plugins for code syntax highlighting
Supporting Packages
  • marked: A Markdown parser used by some Nunjucks filters
  • rehype-highlight: A syntax highlighting plugin for rehype (used with unified-markdown)
  • depcheck: Helps manage dependencies

Version Numbers and the Caret (^)

You might have noticed that each dependency has a version number prefixed with a caret (^):

"metalsmith": "^2.6.3"

The caret means "compatible with version 2.6.3, but allow minor and patch updates." When you run npm install, 'npm' will install the latest version that matches this pattern.

This follows semantic versioning:

  • Major(first number): Breaking changes
  • Minor (second number): New features without breaking changes
  • Patch (third number): Bug fixes

The caret allows your project to automatically get bug fixes and new features without breaking changes.

Understanding the Plugin Ecosystem

Looking at the dependencies, you might notice a pattern: many packages are named metalsmith-[plugin name] or @metalsmith/[plugin name]. These are Metalsmith plugins, each adding specific functionality to the core Metalsmith engine.

The beauty of Metalsmith's plugin-based architecture is that you can add, remove, or replace plugins to customize your build process. Each plugin does one thing well: following the Unix philosophy.

Some common patterns in the plugin names:

  • @metalsmith/[plugin name]: Official plugins maintained by the Metalsmith team
  • metalsmith-[plugin name]: Community plugins created by Metalsmith users

This modular approach means you can start with a simple setup and gradually add more features as needed.

How These Dependencies Are Used

All these dependencies are imported and will be used in metalsmith.js, which we'll explore in detail in the next post.

Adding New Dependencies

As you develop your Metalsmith site, you might want to add new functionality. You can add new dependencies using npm:

npm install --save some-metalsmith-plugin

Then, you'll need to add the plugin to your Metalsmith pipeline in metalsmith.js:

import somePlugin from 'some-metalsmith-plugin';

// ... other imports ...

Metalsmith(__dirname)
  // ... existing plugins ...
  .use(somePlugin())
  // ... more plugins ...

We'll do this in a future post when we add new capabilities to our site. This flexibility is one of Metalsmith's greatest strengths—you can customize it to suit your exact needs.

Exploring Further: Available Plugins

The plugins included in our starter project are just a small selection of what's available in the Metalsmith ecosystem. You can find many more plugins by:

Keeping Dependencies Updated

Over time, dependencies will receive updates with new features and bug fixes. You can update your dependencies using:

npm update

For major version updates (which might include breaking changes), you'll need to:

npm install --save [plugin-name]@latest

Replacing [plugin-name] with the package you want to update. Before you update, it's a good idea to check the plugin's documentation or release notes to see if there are any breaking changes you need to address.

It's a good practice to periodically check for updates and keep your dependencies current for security and performance reasons.

Next Steps

Now that we understand what's in our package.json and the role each dependency plays, we're ready to dive into metalsmith.js in the next post. We'll explore how all these packages come together to build our site, and how the Metalsmith pipeline transforms our content into a complete website.

In the meantime, try modifying one of the scripts or adding a new dependency to see how it affects your project. This hands-on experimentation is the best way to deepen your understanding of how everything works together. Happy building!

Did you know? Metalsmith's plugin architecture was inspired by the Unix philosophy: "Do one thing and do it well." Each plugin focuses on a specific transformation, and they work together to create a powerful, flexible system.

Any comments? Find me on Bluesky.

Scroll to top