npx Nedir ve npm Script Yazımı: Kapsamlı Rehber

Node.js dünyasına adım attığınızda karşınıza çıkan ilk kavramlardan biri npm’dir. Paket kurmak, bağımlılıkları yönetmek derken bir de bakıyorsunuz terminalde npx yazıyor. “Bu ne, npm’den farklı mı?” diye düşündüyseniz yalnız değilsiniz. Hem npx‘in ne işe yaradığını hem de package.json içinde script yazmanın inceliklerini bu yazıda ele alacağız. Günlük geliştirme ve sistem yönetimi iş akışlarınızı ciddi ölçüde hızlandıracak pratik bilgiler sizi bekliyor.

npm ve npx Arasındaki Temel Fark

npm (Node Package Manager), paketleri kurmak ve yönetmek için kullandığınız araçtır. npm install dediğinizde paket ya global sisteminize ya da projenizin node_modules klasörüne iner. Bu sıradan ve anlaşılır bir süreç.

npx ise npm 5.2.0 ile birlikte gelen ve işi biraz farklı ele alan bir araçtır. Paketi kalıcı olarak kurmadan, doğrudan çalıştırmanıza olanak tanır. Şöyle düşünün: bir aracı sadece bir kez kullanacaksınız, sisteminizi gereksiz paketlerle kirletmek istemiyorsunuz. İşte tam bu noktada npx devreye girer.

# npm ile önce kurar sonra çalıştırırsınız
npm install -g create-react-app
create-react-app my-app

# npx ile direkt çalıştırırsınız, kurulum gerekmez
npx create-react-app my-app

İkinci yöntemde create-react-app geçici olarak indirilir, çalıştırılır ve işlem bitince temizlenir. Sisteminizde kalıcı bir iz bırakmaz.

npx Nasıl Çalışır?

npx bir komutu çalıştırmadan önce şu sırayı takip eder:

  • Önce projenizin node_modules/.bin klasörüne bakar
  • Orada yoksa global kurulu paketlere bakar
  • Hiçbirinde yoksa npm registry’den geçici olarak indirir ve çalıştırır

Bu mekanizma sayesinde npx hem yerel hem global hem de henüz kurulmamış paketleri sorunsuzca çalıştırabilir.

# Yerel olarak kurulu bir aracı npx ile çalıştırmak
# node_modules/.bin/eslint yerine:
npx eslint src/index.js

# Belirli bir sürümle çalıştırmak
npx node@18 --version

# Farklı bir registry'den paket çalıştırmak
npx --registry https://registry.npmjs.org cowsay "Merhaba Sysadmin"

Gerçek Dünya Senaryosu: Geçici Araç Kullanımı

Diyelim ki bir CI/CD pipeline kuruyorsunuz ve sadece build aşamasında webpack çalıştırmanız gerekiyor. Global kurulum yapmak istemiyorsunuz çünkü farklı projeler farklı webpack sürümleri kullanıyor olabilir.

# Her proje kendi sürümünü kullanır, çakışma olmaz
npx webpack --config webpack.prod.js

# Belirli bir sürümü zorlamak
npx [email protected] --config webpack.prod.js

Bu yaklaşım özellikle sürüm yönetiminde büyük kolaylık sağlar. Bir sunucuda birden fazla Node projesi yönetiyorsanız, global paketlerin yarattığı sürüm çakışmalarından kurtulursunuz.

package.json ve Script Kavramı

Her Node.js projesinin kalbinde package.json dosyası durur. Bu dosya projenin kimlik kartı gibidir: bağımlılıkları, meta verileri ve en önemlisi scriptleri barındırır.

# Yeni bir proje oluşturduğunuzda
npm init -y

# Oluşan package.json içinde scripts bölümü şöyle görünür:
cat package.json

Aşağıdaki gibi bir yapı göreceksiniz:

{
  "name": "my-project",
  "version": "1.0.0",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "dependencies": {}
}

scripts bölümü, sık kullandığınız komutları kısa takma adlar altında toplamanızı sağlar. npm run komutuyla çalıştırırsınız.

npm Script Yazımının Temelleri

Script yazarken birkaç temel kuralı bilmek işinizi kolaylaştırır.

Basit Script Tanımlamak

# package.json içindeki scripts bölümü
{
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "build": "webpack --config webpack.prod.js",
    "lint": "eslint src/",
    "format": "prettier --write src/**/*.js"
  }
}

Artık şu komutlarla çalıştırabilirsiniz:

npm start          # start ve test için "run" gerekmez
npm test           # bu ikisi özel scriptlerdir
npm run dev        # diğerleri için "run" gerekir
npm run build
npm run lint

Pre ve Post Hook’ları

npm scriptlerinde güçlü bir özellik olan pre ve post hook’ları, bir scriptten önce veya sonra otomatik çalışan komutlar eklemenize olanak tanır. Script adının başına pre veya sonuna post ekleyerek tanımlarsınız.

{
  "scripts": {
    "prestart": "echo 'Sunucu başlatılıyor...' && npm run lint",
    "start": "node server.js",
    "poststart": "echo 'Sunucu başlatıldı!'",
    "prebuild": "rm -rf dist/",
    "build": "webpack --config webpack.prod.js",
    "postbuild": "echo 'Build tamamlandı, dosyalar dist/ klasöründe'"
  }
}

npm start komutunu verdiğinizde sırasıyla prestart, start ve poststart çalışır. Bu sayede build öncesi temizlik, test çalıştırma veya deployment sonrası bildirim gibi işlemleri otomatikleştirebilirsiniz.

Birden Fazla Komutu Zincirleme

Bazen tek bir script içinde birden fazla komut çalıştırmanız gerekir. Bunun için iki yöntem vardır:

&& operatörü ile sıralı çalıştırma: bir komut başarısız olursa durar.

; operatörü ile sıralı çalıştırma: bir komut başarısız olsa bile devam eder.

{
  "scripts": {
    "test:all": "npm run lint && npm run test:unit && npm run test:e2e",
    "clean:build": "rm -rf dist/ && npm run build",
    "deploy": "npm run build && npm run test:all && node scripts/deploy.js"
  }
}

Paralel çalıştırma için ise & (tek) kullanırsınız ya da concurrently paketinden faydalanırsınız:

{
  "scripts": {
    "dev": "concurrently "npm run server" "npm run client"",
    "server": "nodemon backend/server.js",
    "client": "npx webpack serve --config webpack.dev.js"
  }
}
# concurrently paketini kurmak
npm install --save-dev concurrently

Environment Variable Kullanımı

Script yazarken environment variable’ları kullanmak sıkça karşılaşılan bir durumdur. Ancak Linux/Mac ve Windows arasında sözdizimi farklıdır. cross-env paketi bu sorunu çözer.

npm install --save-dev cross-env
{
  "scripts": {
    "start:prod": "cross-env NODE_ENV=production node server.js",
    "start:dev": "cross-env NODE_ENV=development nodemon server.js",
    "test:coverage": "cross-env NODE_ENV=test jest --coverage"
  }
}

Böylece aynı package.json dosyası hem Linux sunucularınızda hem de Windows geliştirici makinelerinde sorunsuz çalışır.

Gelişmiş Script Yazım Teknikleri

npm_lifecycle_event ile Dinamik Script

Scriptler çalışırken npm, otomatik olarak bazı environment variable’lar atar. Bunlardan npm_lifecycle_event hangi scriptin çalıştığını söyler.

# scripts/check-env.js dosyası
const scriptName = process.env.npm_lifecycle_event;
console.log(`Şu an çalışan script: ${scriptName}`);

if (scriptName === 'build:prod' && !process.env.API_KEY) {
  console.error('HATA: API_KEY environment variable tanımlı değil!');
  process.exit(1);
}
{
  "scripts": {
    "build:prod": "node scripts/check-env.js && webpack --config webpack.prod.js"
  }
}

Script Argümanı Geçirmek

-- çift tire ile npm scriptlerine dışarıdan argüman geçirebilirsiniz:

# package.json
{
  "scripts": {
    "test": "jest",
    "lint": "eslint"
  }
}
# Dışarıdan argüman geçirmek
npm run test -- --watch --verbose
npm run lint -- src/components/ --fix

# Bu aslında şu komutları çalıştırır:
# jest --watch --verbose
# eslint src/components/ --fix

Script Dosyalarını Dışarıya Çıkarmak

Karmaşık scriptleri doğrudan package.json içine yazmak okunabilirliği düşürür. Bu durumda ayrı JavaScript dosyaları oluşturmak daha iyi bir pratiktir:

# scripts/deploy.js
const { execSync } = require('child_process');
const environment = process.env.DEPLOY_ENV || 'staging';

console.log(`${environment} ortamına deploy ediliyor...`);

try {
  execSync('npm run build', { stdio: 'inherit' });
  execSync(`rsync -avz dist/ user@${environment}-server:/var/www/app/`, { stdio: 'inherit' });
  console.log('Deploy tamamlandi!');
} catch (error) {
  console.error('Deploy basarisiz:', error.message);
  process.exit(1);
}
{
  "scripts": {
    "deploy:staging": "cross-env DEPLOY_ENV=staging node scripts/deploy.js",
    "deploy:prod": "cross-env DEPLOY_ENV=production node scripts/deploy.js"
  }
}

Gerçek Dünya Senaryosu: Tam Bir Proje Pipeline’ı

Bir web uygulaması geliştirdiğinizi ve hem geliştirme hem de production süreçlerini otomatize etmek istediğinizi düşünelim. İşte gerçekçi bir package.json scripts bölümü:

{
  "scripts": {
    "preinstall": "node --version | grep -q 'v18' || (echo 'Node 18 gerekli!' && exit 1)",
    
    "start": "node dist/server.js",
    "dev": "concurrently "npm run dev:server" "npm run dev:client"",
    "dev:server": "cross-env NODE_ENV=development nodemon src/server.js",
    "dev:client": "npx webpack serve --config webpack.dev.js",
    
    "prebuild": "npm run clean && npm run lint && npm run test",
    "build": "cross-env NODE_ENV=production npx webpack --config webpack.prod.js",
    "postbuild": "npm run build:analyze",
    "build:analyze": "npx webpack-bundle-analyzer dist/stats.json",
    
    "clean": "rm -rf dist/ coverage/ .cache/",
    
    "lint": "eslint src/ --ext .js,.jsx",
    "lint:fix": "eslint src/ --ext .js,.jsx --fix",
    "format": "prettier --write "src/**/*.{js,jsx,css}"",
    
    "test": "jest --passWithNoTests",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage --coverageThreshold='{"global":{"lines":80}}'",
    
    "db:migrate": "node scripts/db-migrate.js",
    "db:seed": "node scripts/db-seed.js",
    "db:reset": "npm run db:migrate && npm run db:seed",
    
    "deploy:staging": "cross-env DEPLOY_ENV=staging node scripts/deploy.js",
    "deploy:prod": "npm run test:coverage && cross-env DEPLOY_ENV=production node scripts/deploy.js"
  }
}

Bu yapıda şunları sağladık:

  • npm run dev ile hem backend hem frontend aynı anda başlar
  • npm run build çalışmadan önce otomatik lint ve test kontrolleri yapılır
  • deploy:prod öncesi %80 test coverage şartı aranır
  • db:reset ile veritabanı tek komutla sıfırlanır

npx ile Yaratıcı Kullanımlar

Yerel Dosya Sunucusu Başlatmak

# Anlık HTTP sunucusu için, kurulum yok
npx http-server ./dist -p 8080 -c-1

# HTTPS ile
npx http-server ./dist -p 8443 --ssl --cert cert.pem --key key.pem

Kod Kalitesi Araçları

# Projeyi analiz etmek için
npx depcheck              # kullanılmayan bağımlılıkları bulur
npx npm-check-updates     # güncellenebilir paketleri listeler

# Paketleri güncellemek
npx npm-check-updates -u  # package.json'ı günceller
npm install               # yeni sürümleri kurar

Script Hata Ayıklama

Scriptlerinizin hangi komutları çalıştırdığını görmek için --if-present ve verbose modunu kullanabilirsiniz:

# Script yoksa hata vermeden devam et
npm run build:optional --if-present

# Detaylı çıktı için
npm run build --loglevel verbose

npm Script Yazımında Dikkat Edilmesi Gerekenler

Scriptlerinizi yazarken sık yapılan hatalardan kaçınmak için şu noktalara dikkat edin:

  • Çapraz platform uyumluluğu: rm -rf Linux/Mac’te çalışır ama Windows’ta çalışmaz. rimraf paketini tercih edin
  • Uzun scriptleri bölün: Tek bir script 3-4 komuttan fazlasını içeriyorsa dışarıya çıkarın
  • Anlamlı isimler verin: build:dev, build:prod, build:analyze gibi namespace kullanın
  • Hata yönetimi: exit 1 ile scriptin başarısız olduğunu açıkça belirtin
  • Gizli bilgiler: API anahtarları asla package.json içine yazılmaz, .env dosyası kullanın
# Çapraz platform temizleme
npm install --save-dev rimraf shx

{
  "scripts": {
    "clean": "rimraf dist/ coverage/",
    "copy:assets": "shx cp -r src/assets dist/assets"
  }
}

Sonuç

npx ve npm scriptleri, Node.js ekosisteminde çalışan herkes için olmazsa olmaz araçlardır. npx sayesinde gereksiz global kurulumlardan kurtulur, sürüm çakışmalarını önler ve CI/CD ortamlarında çok daha temiz bir çalışma alanı elde edersiniz. npm scriptleri ise package.json dosyanızı bir otomasyon merkezi haline getirir: lint, test, build, deploy süreçlerinin tamamını tek bir yerden yönetirsiniz.

Pre/post hook’larını, environment variable’ları ve script zincirleme tekniklerini birleştirdiğinizde, daha önce ayrı shell scriptlerine ya da Makefile’lara sığdırdığınız iş akışlarını standart bir Node.js projesi yapısına entegre edebilirsiniz. Bu hem yeni ekip üyelerinin projeye dahil olmasını kolaylaştırır hem de platform bağımsız bir geliştirme ortamı sağlar.

Bir sonraki adım olarak mevcut projelerinizin package.json dosyalarına göz atın. Tekrar eden terminal komutlarınızı script haline getirin ve npx ile geçici araç çalıştırmanın ne kadar pratik olduğunu bizzat deneyimleyin. Bir süre sonra fark edeceksiniz ki terminalde yazdığınız komutların büyük çoğunluğu artık npm run ile başlıyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir