Logo

dev-resources.site

for different kinds of informations.

Make your package.json readable again by humans: organizing npm scripts with shell files

Published at
9/30/2024
Categories
node
npm
bash
refactoring
Author
alalbers
Categories
4 categories in total
node
open
npm
open
bash
open
refactoring
open
Author
8 person written this
alalbers
open
Make your package.json readable again by humans: organizing npm scripts with shell files

In Node.js projects, the package.json file often serves as a convenient repository for our npm scripts. While one-liners within the scripts section work perfectly fine for simple tasks, the situation becomes less ideal when scripts evolve into complex sequences. Think about scenarios involving multiple steps, conditional logic, robust error handling, or the need for comprehensive logging.

The problem with one-liners

Let's consider a common example: a deploy script within your package.json.

{
  "name": "example-01",
  "description": "A really long one-line deploy script."
  "version": "1.0.0",
  "scripts": {
    "deploy": "rm -rf dist && mkdir dist && cp -r src/* dist && cd dist && zip -r ../my-project.zip * && cd .. && scp my-project.zip user@server:/path/to/deploy"
  }
}
Enter fullscreen mode Exit fullscreen mode

At first glance, this deploy script seems to get the job done. However, it quickly becomes unwieldy. The entire process is crammed into a single line, making it a challenge to read, understand, and maintain.

The problem escalates when you try to introduce error checks and logging:

{
  "name": "example-02",
  "description": "Attempt to add error checks and logging to the one-line deploy script."
  "version": "1.0.0",
  "scripts": {
    "deploy": "[ -d \"dist\" ] || (echo \"Error: 'dist' directory not found.\" && exit 1) && rm -rf dist && echo \"Removed dist directory\" || (echo \"Failed to remove dist directory\" && exit 1) && mkdir dist && echo \"Created dist directory\" || (echo \"Failed to create dist directory\" && exit 1) && cp -r src/* dist && echo \"Copied files to dist\" || (echo \"Failed to copy files to dist\" && exit 1) && cd dist && echo \"Changed directory to dist\" || (echo \"Failed to change directory to dist\" && exit 1) && zip -r ../my-project.zip * && echo \"Created zip archive\" || (echo \"Failed to create zip archive\" && exit 1) && cd .. && echo \"Changed directory back to project root\" || (echo \"Failed to change directory back to project root\" && exit 1) && scp my-project.zip user@server:/path/to/deploy && echo \"Deployed to server\" || (echo \"Failed to deploy to server\" && exit 1)"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, you're dealing with a visually cluttered script filled with escaped double quotes. Even worse, the exit 1 statements within the chained && commands won't actually halt the entire script's execution as intended. This creates a false sense of security and can lead to unexpected issues during deployment.

A cleaner solution: dedicated shell scripts

A straightforward and effective strategy for managing complex npm scripts is to move them out of your package.json and into dedicated shell files. Let's refactor our deploy script to demonstrate this approach:

1. Create a scripts directory

If you don't already have one, create a scripts directory within your project root.

2. Move the script into its own file

Cut the one-liner out of package.json and create a new file named deploy.sh inside the scripts directory:

#!/bin/sh

# Check if the 'dist' directory exists
if [ ! -d "dist" ]; then
    echo "Error: 'dist' directory not found."
    exit 1
fi

# Remove the 'dist' directory
rm -rf dist
if [ $? -ne 0 ]; then
    echo "Failed to remove dist directory"
    exit 1
else
    echo "Removed dist directory"
fi

# Create a new 'dist' directory
mkdir dist
if [ $? -ne 0 ]; then
    echo "Failed to create dist directory"
    exit 1
else
    echo "Created dist directory"
fi

# Copy files from 'src' to 'dist'
cp -r src/* dist
if [ $? -ne 0 ]; then
    echo "Failed to copy files to dist"
    exit 1
else
    echo "Copied files to dist"
fi

# Change directory to 'dist'
cd dist
if [ $? -ne 0 ]; then
    echo "Failed to change directory to dist"
    exit 1
else
    echo "Changed directory to dist"
fi

# Create a zip archive of the 'dist' directory
zip -r ../my-project.zip *
if [ $? -ne 0 ]; then
    echo "Failed to create zip archive"
    exit 1
else
    echo "Created zip archive"
fi

# Change directory back to the project root
cd ..
if [ $? -ne 0 ]; then
    echo "Failed to change directory back to project root"
    exit 1
else
    echo "Changed directory back to project root"
fi

# Deploy the zip archive to the server
scp my-project.zip user@server:/path/to/deploy
if [ $? -ne 0 ]; then
    echo "Failed to deploy to server"
    exit 1
else
    echo "Deployed to server"
fi
Enter fullscreen mode Exit fullscreen mode

3. Update your package.json

Replace the full one-liner script with a call to execute your new deploy.sh file.

{
    "name": "my-react-app",
    "version": "1.0.0",
    "scripts": {
        "deploy": "sh scripts/deploy.sh"
    }
}
Enter fullscreen mode Exit fullscreen mode

Benefits of this Approach

  • Readability and Maintainability: The shell script is well-organized with proper indentation and comments, making it easier to read, understand, and modify in the future.
  • Robust Error Handling: The exit 1 statements now function correctly, ensuring that the script stops immediately if an error occurs.
  • Separation of Concerns: By keeping complex scripts in separate files, you maintain a cleaner package.json and promote better code organization within your project.

In Conclusion

While package.json is a handy place for simple npm scripts, don't hesitate to leverage dedicated shell scripts for complex tasks. This approach enhances code clarity, maintainability, and error handling, contributing to a smoother development experience.

refactoring Article's
30 articles in total
Favicon
Python: Refactoring to Patterns
Favicon
STOP Writing Dirty Code: Fix The Data Class Code Smell Now!
Favicon
7 Ways to Refactor Your Flutter Application
Favicon
Những Tip đơn giản giúp đoạn code trở nên CLEAR và MAINTAIN dễ hơn
Favicon
Refactoring 021 - Remove Dead Code
Favicon
React v19, unboxing! 📦
Favicon
De software legacy a oportunitat estratègica: El punt de partida (I)
Favicon
The Backend Testing Breakup 💔
Favicon
Simplifying your code: replacing heavy packages with lightweight solutions
Favicon
The Rewrite Trap: A Programmer's Survival Guide
Favicon
Refactoring: The Art of Polishing Code
Favicon
El verdadero valor del software no siempre está en el presente
Favicon
Refactoring 019 - Reify Email Addresses
Favicon
How LLMs Revolutionize Coding Efficiency
Favicon
Effective Strategies for Refactoring a Large Codebase: Best Practices and Approaches
Favicon
Convert a ReactJS app from Vite to Nx
Favicon
Simplifying Conditional Expressions in C#: Tips for Clean and Readable Code
Favicon
Simplifying Method Calls in C#: Techniques for Cleaner and More Maintainable Code
Favicon
Mastering Clean Code: 4 Essential Techniques for Organizing Data in C#
Favicon
Refactoring: Moving Features Between Objects
Favicon
Composing Methods: A Practical Guide to Clean Code
Favicon
Refactoring 017 - Convert Attributes to Sets
Favicon
Como Refatorar Seu Código Com Muitos If-Else? Part 2
Favicon
Stop Re-Implementing Code: Use Extract Method in Rust
Favicon
De lo funcional hasta lo legible con Tidyings
Favicon
The Double-Edged Sword of IEnumerable and yield return in C#
Favicon
In Theory: Self-Correcting Software - Part 2
Favicon
Make your package.json readable again by humans: organizing npm scripts with shell files
Favicon
Transforming DevEx Through Bulk Refactoring (and How AI Can Assist)
Favicon
Refactoring for Efficiency: Tackling Performance Issues in Data-Heavy Pages

Featured ones: