Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/Production dependency Optimization By running the command 'npm prune --omit=dev' #2848

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"import:sample-data": "tsx ./src/utilities/loadSampleData.ts",
"import:sample-data:prod": "node ./build/utilities/loadSampleData.js",
"gen:schema": "graphql-inspector introspect ./src/typeDefs/**/**/*.ts --write ./schema.graphql ",
"update:toc": "node scripts/githooks/update-toc.js"
"update:toc": "node scripts/githooks/update-toc.js",
"move-and-prune": "./scripts/deps/analyze-dependencies.sh && ./scripts/deps/move-from-diff.sh && npm prune --omit=dev"
},
"repository": {
"type": "git",
Expand Down
117 changes: 117 additions & 0 deletions scripts/deps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Dependency Analysis and Management Scripts

This document consolidates the steps and explanations for analyzing and managing dependencies in a Node.js project using the provided scripts.

---

## Overview

These scripts help analyze the difference between production and development dependencies and ensure that all required dependencies are correctly categorized in the `package.json` file. They also support moving dev-only dependencies used in production to the `dependencies` section.

### Purpose
1. **Analyze Dependencies:** Identify dev-only and prod-only dependencies.
2. **Categorize Dependencies:** Ensure all production-required dependencies are moved from `devDependencies` to `dependencies`.
3. **Prune Unused Dev Dependencies:** Reduce the size of the final production build.

---

## Prerequisites

1. **Install `jq`**
- `jq` is required for processing JSON files. Install it using the following commands based on your operating system:
- **Alpine Linux**: `apk add --no-cache jq`
- **Debian/Ubuntu**: `apt-get update && apt-get install -y jq`
- **RHEL/CentOS**: `yum install -y jq`
- **MacOS (with Homebrew)**: `brew install jq`
- **Windows**: Download the appropriate binary from [jq's official website](https://stedolan.github.io/jq/download/).

2. **Ensure Scripts Are Executable**
- Make the scripts executable:
```sh
chmod +x ./scripts/deps/analyze-dependencies.sh
chmod +x ./scripts/deps/move-from-diff.sh
```

3. **Set Up npm**
- Ensure `npm` is installed and configured properly in the environment.

4. **Optional: Simplified Execution**
- If you don't want to run the scripts manually, use the following command, which is defined in `package.json`:
```sh
npm run move-and-prune
```
- This command runs both scripts and executes `npm prune --omit=dev` to remove unnecessary development dependencies.

---

## Usage

### Step 1: Analyze Dependencies

1. **Run the `analyze-dependencies.sh` Script**
- Navigate to the project root directory (where `package.json` is located).
- Execute:
```sh
./scripts/deps/analyze-dependencies.sh
```
- **What It Does:**
- Generates the following files in the `scripts/deps` folder:
- `prod-deps.json`: Top-level production dependencies.
- `dev-deps.json`: Top-level dev+prod dependencies.
- `prod-deps-keys.json`: Keys of production dependencies.
- `dev-deps-keys.json`: Keys of dev+prod dependencies.
- Displays the differences between production and dev dependencies.

2. **Output Files**
- The results are saved in the `scripts/deps` folder for further processing.

---

### Step 2: Move Required Dev Dependencies

1. **Run the `move-from-diff.sh` Script**
- Execute:
```sh
./scripts/deps/move-from-diff.sh
```
- **What It Does:**
- Compares `prod-deps-keys.json` and `dev-deps-keys.json`.
- Identifies dev-only dependencies used in the production code.
- Moves required dependencies from `devDependencies` to `dependencies` in `package.json`.

2. **Backup of `package.json`**
- Before making changes, the script creates a backup file `package.json.bak` for safety.

3. **Validation**
- The script checks if a dependency is used in the production code (default: `./src` folder).
- Modify the script if your codebase structure differs.

---

### Step 3: Prune Unused Dev Dependencies

1. **Run npm prune**
- After moving dependencies, remove unused dev dependencies:
```sh
npm prune --omit=dev
```
- **Purpose:**
- Ensures the `node_modules` folder contains only production dependencies.

---

## Combined Execution

To automate the above steps:

- Use the `npm run move-and-prune` command, which:
1. Runs both `analyze-dependencies.sh` and `move-from-diff.sh`.
2. Executes `npm prune --omit=dev` to clean up unused development dependencies.

---

## Notes

- Ensure your production code resides in `./src` folders. Update the scripts if your codebase structure differs.
- Always test the changes locally before deploying them to production to avoid runtime errors.

85 changes: 85 additions & 0 deletions scripts/deps/analyze-dependencies.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/sh
set -e

# Function to check and install jq
install_jq_if_missing() {
echo "Checking if jq is installed..."
if ! command -v jq >/dev/null 2>&1; then
echo "jq is not installed."
echo "Error: jq is required but not installed. Please install jq first:"
echo "Alpine: apk add --no-cache jq"
echo "Debian/Ubuntu: apt-get update && apt-get install -y jq"
echo "RHEL/CentOS: yum install -y jq"
echo "MacOS (with Homebrew): brew install jq"
echo "Windows: Download jq from https://stedolan.github.io/jq/download/"
exit 1
else
echo "jq is already installed."
fi
PurnenduMIshra129th marked this conversation as resolved.
Show resolved Hide resolved
}

# Run the jq installation check
install_jq_if_missing

echo "Checking if package.json exists..."
if [ ! -f "package.json" ]; then
echo "Error: No package.json found in the current directory."
exit 1
fi

# Create the deps folder if it does not exist
DEPS_FOLDER="./scripts/deps"
echo "Creating the $DEPS_FOLDER folder for storing dependency files..."
mkdir -p "$DEPS_FOLDER"

# Check if .env file exists and load it
if [ -f ".env" ]; then
. ./.env
else
echo "Warning: .env file not found"
fi

# Check if NODE_ENV is set
if [ -z "$NODE_ENV" ]; then
echo "Warning: NODE_ENV is not set"
NODE_ENV="development" # Set default to development
fi

echo "Current NODE_ENV: $NODE_ENV"

# Check for production environment
if [ "$NODE_ENV" = "production" ]; then
echo "Skipping npm install in production environment"
else
echo "Installing dependencies (if necessary) to ensure npm ls commands run accurately..."
npm install --no-save
fi

echo "Generating list of production dependencies (top-level only)..."
if ! npm ls --omit=dev --json > "$DEPS_FOLDER/prod-deps.json" 2>/dev/null; then
echo "Warning: npm ls command produced errors, results may be incomplete"
fi

echo "Generating list of dev+prod dependencies (top-level only)..."
if ! npm ls --prod=false --json > "$DEPS_FOLDER/dev-deps.json" 2>/dev/null; then
echo "Warning: npm ls command produced errors, results may be incomplete"
fi
PurnenduMIshra129th marked this conversation as resolved.
Show resolved Hide resolved

# Extract just the top-level dependencies' keys. If a key is absent, we fall back to an empty object "{}".
echo "Extracting top-level dependencies from prod-deps.json..."
jq '.dependencies? // {} | keys' "$DEPS_FOLDER/prod-deps.json" > "$DEPS_FOLDER/prod-deps-keys.json"
echo "Extracting top-level dependencies from dev-deps.json..."
jq '.dependencies? // {} | keys' "$DEPS_FOLDER/dev-deps.json" > "$DEPS_FOLDER/dev-deps-keys.json"

# Sort them and compare side by side. We'll use textual diff to highlight any differences.
echo "Comparing top-level production vs. dev dependencies..."
sort "$DEPS_FOLDER/prod-deps-keys.json" -o "$DEPS_FOLDER/prod-deps-keys.json"
sort "$DEPS_FOLDER/dev-deps-keys.json" -o "$DEPS_FOLDER/dev-deps-keys.json"
diff "$DEPS_FOLDER/prod-deps-keys.json" "$DEPS_FOLDER/dev-deps-keys.json" || true

echo ""
echo "=========================================="
echo "Finished analyzing dependencies."
echo "$DEPS_FOLDER/prod-deps-keys.json: top-level prod deps."
echo "$DEPS_FOLDER/dev-deps-keys.json: top-level dev+prod deps."
echo "Use 'diff $DEPS_FOLDER/prod-deps-keys.json $DEPS_FOLDER/dev-deps-keys.json' for side-by-side comparison."
79 changes: 79 additions & 0 deletions scripts/deps/move-from-diff.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/bin/sh
set -e

# Define the folder where dependency files are stored
DEPS_FOLDER="./scripts/deps"

echo "Analyzing dependencies from diff output..."

# Check if the necessary files exist in the deps folder
if [ ! -f "$DEPS_FOLDER/prod-deps-keys.json" ] || [ ! -f "$DEPS_FOLDER/dev-deps-keys.json" ]; then
echo "Error: prod-deps-keys.json or dev-deps-keys.json not found in the $DEPS_FOLDER folder. Run analyze-dependencies.sh first."
exit 1
fi

# Generate the diff and extract dev-only packages
DIFF_OUTPUT=$(diff "$DEPS_FOLDER/prod-deps-keys.json" "$DEPS_FOLDER/dev-deps-keys.json" || true)
DEV_ONLY_PACKAGES=$(echo "$DIFF_OUTPUT" | grep '^>' | sed 's/^> //' | tr -d '",')

if [ -z "$DEV_ONLY_PACKAGES" ]; then
echo "No dev-only dependencies found to check."
exit 0
fi

echo "Found dev-only dependencies to check for production usage:"
echo "$DEV_ONLY_PACKAGES"

# Temporary file to store required production dependencies
REQUIRED_FOR_PRODUCTION_FILE=$(mktemp)

# Allow configuration of source directory
SOURCE_DIR=${SOURCE_DIR:-"./src"}

for PACKAGE in $DEV_ONLY_PACKAGES; do
echo "Checking if $PACKAGE is used in production..."

# Use grep to check if the package name appears in the production code
if grep -qr "$PACKAGE" "$SOURCE_DIR"; then
echo "$PACKAGE is required in production."
echo "$PACKAGE" >> "$REQUIRED_FOR_PRODUCTION_FILE"
else
echo "$PACKAGE is not used in production."
fi
done

if [ ! -s "$REQUIRED_FOR_PRODUCTION_FILE" ]; then
echo "No dev dependencies are required in production."
rm -f "$REQUIRED_FOR_PRODUCTION_FILE"
exit 0
fi

echo "The following dev dependencies are required in production:"
cat "$REQUIRED_FOR_PRODUCTION_FILE"

# Backup package.json
echo "Creating backup of package.json..."
cp package.json package.json.bak

# Move required dependencies to production dependencies
while IFS= read -r PACKAGE; do
echo "Moving $PACKAGE to dependencies..."

# First, remove the package from devDependencies
if ! npm uninstall "$PACKAGE" --save-dev; then
echo "Error: Failed to uninstall $PACKAGE from devDependencies"
exit 1
fi
# Install as a production dependency
if ! npm install "$PACKAGE" --save-prod; then
echo "Error: Failed to install $PACKAGE as production dependency"
echo "Warning: Package is now removed from both devDependencies and dependencies"
exit 1
fi

done < "$REQUIRED_FOR_PRODUCTION_FILE"

PurnenduMIshra129th marked this conversation as resolved.
Show resolved Hide resolved
# Clean up
rm -f "$REQUIRED_FOR_PRODUCTION_FILE"

echo "Required dev dependencies successfully moved to production dependencies."
Loading