Task Runner'lar ve Modern Bundling'in Doğuşu
Grunt'ın build automation'ı nasıl dönüştürdüğü ve Webpack'in dependency'ler hakkında düşünme şeklimizi nasıl devrim yaptığı. Frontend development'ı sonsuza dek değiştiren manuel processlerden sofistike bundling'e acı verici geçiş.
2012'ye kadar, manuel build processlerinin problemleri dayanılmaz hale gelmişti. Bir developer'ın yazdığı ve başka kimsenin anlamadığı 200 satırlık Bash dosyasının "deployment script" olduğu bir projede çalıştığımı hatırlıyorum. O developer şirketten ayrıldığında, herhangi bir şeyi değiştirmekten korkuyorduk. Build salı günü çalıştırırsanız gizemli bir şekilde fail oluyordu ve çözümümüz... salı günleri deploy etmeyin.
Grunt'ın ortaya çıktığı ortam buydu ve devrimci hissettirdi. İlk kez, karmaşık projeleri handle edecek kadar configure edilebilirken sıkıcı, hata eğilimli işleri otomatize edebilen bir tool'umuz vardı. Ama bu serideki her tool gibi, Grunt bir dizi problemi çözerken tamamen yeni olanları ortaya çıkardı.
Grunt Devrimi (2012-2015)#
Ben Alman 2012'de Grunt'ı release ettiğinde, temel bir şeyi ele alıyordu: build processlerinin imperative değil declarative olması gerekiyordu. Farklı makinelerde farklı çalışabilecek shell script'ler yazmak yerine, ne olmasını istediğinizi tarif ediyordunuz.
Scripting Üzerinde Configuration#
Tipik bir Gruntfile şöyleydi:
module.exports = function(grunt) {
grunt.initConfig({
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dist/built.js'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/built.min.js': ['<%= concat.dist.dest %>']
}
}
},
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true,
console: true,
module: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
};
Bu çok büyüktü. İlk kez, bir projeye bakıp build process sırasında tam olarak ne olduğunu anlayabiliyordunuz. Artık gizemli shell script'ler yok, build'i yazan kişinin bunu düzgün dokümante etmiş olması için dua etmek yok.
Plugin Ecosystem Patlaması#
Grunt'ın dehası, build task'lerinin pattern'leri takip ettiğini fark etmekti. Sass compile etmek mi lazım? grunt-contrib-sass
var. Resimleri optimize etmek mi istiyorsun? grunt-contrib-imagemin
. S3'e deploy etmek mi gerekiyor? grunt-aws-s3
.
2013'e kadar yüzlerce Grunt plugin'i vardı. Neredeyse her şeyi otomatize edebiliyordunuz:
- CSS preprocessing (Sass, Less, Stylus)
- JavaScript linting ve minification
- Image optimization
- File copying ve watching
- Template compilation
- Testing framework'leri
- Deployment processleri
Gerçek Dünya Etkisi: Build'lerin Gerçekten Çalıştığı İlk Zaman#
Grunt'ı başarıyla kurduğumuz ilk projeyi hatırlıyorum. Deployment processimiz "parmaklarınızı çaprazlayın ve umutsuzlukla bekleyin"den "grunt build
çalıştırın ve kahve alın"e geçti. Psikolojik etki derinidi—kendi tooling'imizden korkmayı bıraktık.
Performance kazanımları anındaydı:
- Build zamanı: 15 dakika manuel işten 2 dakika otomatik process'e
- Hata oranları: İnsan hatası elimine edildiği için build failure'ları %80 düştü
- Deployment güveni: Haftada bir kez yerine günde birkaç kez deploy edebiliyorduk
Ama daha da önemlisi, Grunt modern tool'ların hala takip ettiği pattern'i kurdu: code üzerinde configuration, plugin-based architecture ve development ile production build'leri arasında net ayrım.
Grunt'ın Zorlandığı Yerler#
Projeler büyüdükçe, Grunt'ın limitasyonları belirginleşti:
Configuration Cehennemi: Karmaşık Gruntfile'lar maintain edilemez hale geldi. Üzerinde çalıştığım bir production projesinden gerçek örnek:
// Bu sadece 400 satırlık Gruntfile'ın CSS kısmıydı
sass: {
options: {
sourceMap: true,
outputStyle: 'compressed'
},
dev: {
files: {
'dist/css/main.css': 'src/scss/main.scss',
'dist/css/admin.css': 'src/scss/admin.scss',
'dist/css/mobile.css': 'src/scss/mobile.scss'
}
},
prod: {
options: {
sourceMap: false,
outputStyle: 'compressed'
},
files: {
'dist/css/main.min.css': 'src/scss/main.scss',
'dist/css/admin.min.css': 'src/scss/admin.scss',
'dist/css/mobile.min.css': 'src/scss/mobile.scss'
}
}
},
autoprefixer: {
options: {
browsers: ['last 3 versions', 'ie 8', 'ie 9']
},
dev: {
src: 'dist/css/*.css'
},
prod: {
src: 'dist/css/*.min.css'
}
},
cssmin: {
options: {
advanced: false,
keepSpecialComments: 0
},
prod: {
files: [{
expand: true,
cwd: 'dist/css/',
src: ['*.css', '!*.min.css'],
dest: 'dist/css/',
ext: '.min.css'
}]
}
}
Her Yerde Geçici Dosyalar: Grunt'ın task-based yaklaşımı her adımın diske yazması demekti. Tipik bir build onlarca geçici dosya yaratabilir, bu da yavaş ve debug etmesi zor olmasına yol açıyordu.
Incremental Processing Yok: Bir dosyayı değiştir, her şeyi yeniden build et. Bu, projeler yüzlerce dosyaya ulaştığında sürdürülebilir değildi.
Gulp Yanıtı: Stream'ler ve Hız (2013-2016)#
Eric Schoffstall tarafından yaratılan Gulp, temelden farklı bir yaklaşım aldı. Configuration yerine code'u vurguladı. Dosyalar yerine stream'leri kullandı.
Stream Devrimi#
const gulp = require('gulp');
const sass = require('gulp-sass');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
const autoprefixer = require('gulp-autoprefixer');
gulp.task('styles', function() {
return gulp.src('src/scss/**/*.scss')
.pipe(sass())
.pipe(autoprefixer('last 3 versions'))
.pipe(gulp.dest('dist/css'));
});
gulp.task('scripts', function() {
return gulp.src('src/js/**/*.js')
.pipe(concat('app.js'))
.pipe(uglify())
.pipe(gulp.dest('dist/js'));
});
gulp.task('watch', function() {
gulp.watch('src/scss/**/*.scss', ['styles']);
gulp.watch('src/js/**/*.js', ['scripts']);
});
gulp.task('default', ['styles', 'scripts', 'watch']);
Avantajlar anındaydı:
- Daha hızlı build'ler: Geçici dosya yok, her şey memory'de oluyordu
- Daha sezgisel: Pipe metaforu developer'ların data transformation hakkında düşünme şekliyle uyuşuyordu
- Daha iyi error handling: Stream'ler hataları handle etmeyi ve report etmeyi kolaylaştırdı
- Incremental processing: Sadece değişen dosyalar process ediliyordu
Gulp Neden Kazandı (Geçici Olarak)#
Gulp, configuration'dan çok programming gibi hissettirdiği için büyük kabul gördü. Developer'lar karmaşık build senaryolarını handle etmek için JavaScript mantığını kullanabiliyorlardı:
gulp.task('scripts', function() {
const isProduction = process.env.NODE_ENV === 'production';
let stream = gulp.src('src/js/**/*.js')
.pipe(concat('app.js'));
if (isProduction) {
stream = stream.pipe(uglify());
}
return stream.pipe(gulp.dest('dist/js'));
});
Gerçek dünya performance iyileştirmeleri:
- Build zamanı: Eşdeğer Grunt task'lerinden %40-60 daha hızlı
- Memory kullanımı: Stream processing nedeniyle %50 azalma
- Watch modu: Incremental değişiklikler için neredeyse anında rebuild'ler
Module Problemi Ortaya Çıkıyor#
Hem Grunt hem Gulp build automation problemini çözdü, ama daha derin bir sorunu ortaya çıkardılar: JavaScript'in native module sistemi yoktu. Dosyaları concatenate edebiliyordunuz, ama dependency'leri hala manuel yönetmek zorundaydınız.
2013'ten bu yaygın pattern'i düşünün:
// utils.js'de
var Utils = {
formatDate: function(date) { /* ... */ },
parseJSON: function(str) { /* ... */ }
};
// models.js'de (utils.js'e bağımlı)
var User = {
create: function(data) {
var parsed = Utils.parseJSON(data);
// ...
}
};
// views.js'de (models.js ve utils.js'e bağımlı)
var UserView = {
render: function(user) {
var date = Utils.formatDate(user.createdAt);
// ...
}
};
Dependency sırası hala manualdi:
<script src="js/utils.js"></script>
<script src="js/models.js"></script>
<script src="js/views.js"></script>
<script src="js/app.js"></script>
Sırayı değiştir, uygulamayı boz. Bu problem, uygulamalar büyüdükçe çok daha kötüye gidecekti.
Module System Savaşları (2009-2014)#
Grunt ve Gulp build automation'ı çözerken, paralel bir evrim yaşanıyordu: JavaScript sonunda module sistemler alıyordu. Problem şuydu ki üç farklı yaklaşım ortaya çıktı, her birinin farklı felsefeleri vardı.
CommonJS: Server-Side Düşünce#
Node.js tarafından popülerleştirilen CommonJS, synchronous require()
çağrıları kullanıyordu:
// math.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
module.exports = {
add: add,
multiply: multiply
};
// app.js
var math = require('./math');
console.log(math.add(1, 2)); // 3
Bu, dosyaların local olduğu Node.js için mükemmel çalışıyordu, ama browser'lar UI'yi bloke etmeden module'leri synchronous yükleyemiyordu.
AMD: Asynchronous Module Definition#
RequireJS, asynchronous loading'i handle etmek için AMD'yi tanıttı:
// math.js
define(function() {
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
return {
add: add,
multiply: multiply
};
});
// app.js
require(['./math'], function(math) {
console.log(math.add(1, 2)); // 3
});
AMD browser loading problemini çözdü ama birçok developer'ın doğal bulmadığı verbose, callback-heavy kodla sonuçlandı.
UMD: Universal Module Definition#
UMD her yerde çalışan module'ler yaratmaya çalıştı:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['exports'], factory);
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
// CommonJS
factory(exports);
} else {
// Browser globals
factory((root.myModule = {}));
}
}(typeof self !== 'undefined' ? self : this, function (exports) {
function add(a, b) {
return a + b;
}
exports.add = add;
}));
UMD her yerde çalışıyordu ama o kadar verbose'tu ki genellikle elle yazmak yerine tool'lar tarafından generate ediliyordu.
Gerçek Dünya Kaosu#
Pratikte, çoğu proje module formatlarının bir karışımıyla sonuçlanıyordu. Tipik bir proje şunlara sahip olabilirdi:
- AMD kullanan third-party kütüphaneler (RequireJS ekosistemi)
- CommonJS kullanan server-side kod (Node.js modülleri)
- Global değişkenler kullanan legacy kod
- Takımın "standart" olarak karar verdiği her neyse onu kullanmaya çalışan yeni kod
2013'te üzerinde çalıştığım bir projede, uygulama kodu için RequireJS'imiz vardı, ama global $
bekleyen jQuery plugin'leri ve build script'leri için Node.js modülleri vardı. Bunu çalıştırmak için configuration dosyası 150 satırdı ve kimse bunu tamamen anlamıyordu.
Browserify: Browser'da Node.js Modülleri (2011-2016)#
James Halliday (substack) Browserify ile radikal bir yaklaşım aldı: yeni bir module formatı yaratmak yerine, sadece CommonJS'in browser'da çalışmasını sağla.
Transform Devrimi#
# Node.js gibi dependency'leri yükle
npm install underscore jquery
# Node.js gibi kod yaz
# app.js
var _ = require('underscore');
var $ = require('jquery');
$('#app').html(_.template('<h1>Hello <%= name %>!</h1>')({ name: 'World' }));
# Browser için bundle et
browserify app.js -o bundle.js
Bu devrimciydi çünkü:
- Bir module formatı: Artık AMD vs CommonJS vs UMD kararları yok
- npm ekosistemi: Browser'da binlerce Node.js modülüne erişim
- Tanıdık syntax: Developer'lar Node.js'ten CommonJS'i zaten biliyorlardı
- Transform pipeline: Plugin'ler bundling sırasında kodu modify edebiliyordu
Transform'lar: İlk Bundle Processing Pipeline#
Browserify'ın transform sistemi modern webpack loader'larının öncüsüydü:
# ES6'yı ES5'e transform et
browserify app.js -t babelify -o bundle.js
# CoffeeScript'i transform et
browserify app.coffee -t coffeeify -o bundle.js
# Template'leri transform et
browserify app.js -t hbsfy -o bundle.js
Sofistike processing pipeline'ları yaratmak için transform'ları chain edebiliyordunuz:
browserify app.js \
-t [ babelify --presets es2015 ] \
-t envify \
-t uglifyify \
-o bundle.js
npm + Browserify Ekosistemi#
İlk kez, frontend development backend development ile aynı package ekosistemini kullanabiliyordu. Date manipulation mı istiyorsun? npm install moment
. HTTP request'leri mi lazım? npm install axios
.
Bu olumlu bir döngü yarattı:
- Daha fazla package "isomorphic" hale geldi (hem Node.js'te hem browser'larda çalıştı)
- Frontend projeler savaş testinden geçmiş server-side kütüphaneleri leverage edebilirdi
- JavaScript ekosistemi npm etrafında birleşti
Browserify'ın Limitlerini Bulduğu Yerler#
Uygulamalar büyüdükçe, Browserify'ın basitliği bir limitasyon haline geldi:
Bundle Size Sorunları: Browserify sadece bir fonksiyon kullansanız bile tüm modülleri include ediyordu. Sadece _.map
kullanmak için full Lodash kütüphanesini yüklemek büyük bundle'larla sonuçlanıyordu.
Code Splitting Yok: Her şey tek bundle.js dosyasına gidiyordu. Büyük uygulamalar multi-megabyte bundle'larla sonuçlanıyordu.
Asset Management Yok: Browserify JavaScript'i handle ediyordu, ama CSS, resimler ve diğer asset'ler hala ayrı tooling'e ihtiyaç duyuyordu.
Build Performance: Büyük projeler, incremental compilation olmadan bundle etmek dakikalar sürebiliyordu.
Webpack: Game Changer (2012-Şimdi)#
Tobias Koppers webpack'i temelden farklı bir felsefeyle yarattı: her şeyi module olarak ele al. Sadece JavaScript değil—CSS, resimler, fontlar, her şey.
Her Şey Bir Module#
// JavaScript modülleri (tanıdık)
import utils from './utils.js';
// CSS modülleri (devrimci)
import './styles.css';
// Image modülleri (akıl almaz)
import logo from './logo.png';
// JSON modülleri
import config from './config.json';
// Hatta HTML template'leri
import template from './template.html';
Bu yaklaşım birden fazla problemi aynı anda çözdü:
- Dependency tracking: webpack hangi dosyaların gerekli olduğunu tam olarak biliyordu
- Dead code elimination: Kullanılmayan dosyalar bundle'a dahil edilmiyordu
- Cache busting: File hash'leri otomatik generate ediliyordu
- Asset optimization: Resimler otomatik optimize edilebilir, inline edilebilir veya convert edilebiliyordu
Loader Sistemi#
webpack'in loader sistemi Browserify transform'larından ilham aldı ama çok daha güçlüydü:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
}
]
}
};
Code Splitting: Performance Breakthrough#
webpack dynamic import'lara dayalı otomatik code splitting tanıttı:
// Dynamic import ayrı bundle yaratır
import('./heavy-feature.js').then(module => {
module.initialize();
});
// Multiple entry point'ler multiple bundle yaratır
module.exports = {
entry: {
app: './src/app.js',
admin: './src/admin.js'
}
};
Bu, Browserify'ın handle edemediği bundle size problemini çözdü. Uygulamalar minimal kodu upfront yükleyebilir ve ek feature'ları on demand fetch edebilirdi.
Development Experience Devrimi#
webpack-dev-server Hot Module Replacement (HMR) tanıttı:
// Bu dosyadaki değişiklikler browser'ı refresh etmeden güncelliyor
if (module.hot) {
module.hot.accept('./component.js', function() {
// Component'i yerinde güncelle
updateComponent();
});
}
Productivity etkisi muazzamdı:
- CSS değişiklikleri anındaydı (page refresh yok)
- JavaScript değişiklikleri uygulama state'ini korudu
- Source map'lerle debugging çok daha kolay hale geldi
- Incremental compilation ile development build'leri hızlıydı
Configuration Karmaşıklığı: Gücün Bedeli#
webpack'in gücü karmaşıklığın bedeli geldi. 2015'te tipik bir webpack config:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
app: './src/app.js',
vendor: ['react', 'react-dom', 'lodash']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[chunkhash].js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
},
{
test: /\.(png|svg|jpg|gif)$/,
use: {
loader: 'file-loader',
options: {
name: '[path][name].[hash].[ext]'
}
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new ExtractTextPlugin('[name].[contenthash].css'),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
})
],
resolve: {
modules: [
path.resolve(__dirname, 'src'),
'node_modules'
]
}
};
Bu configuration gerekliydi ama korkutucu. Birçok developer karmaşıklığı nedeniyle webpack'ten kaçınıyordu, bu da Create React App gibi "zero-config" tool'ların yükselişine yol açtı.
Ekosistem Yakınsaması (2015-2018)#
2015'e kadar, frontend tooling ekosistemi birkaç temel prensip etrafında yakınsamıştı:
Universal Package Manager Olarak npm#
Bower esasen ölmüştü. npm şu şekilde package management savaşını kazanmıştı:
- Hem frontend hem backend package'ları destekliyordu
- Nested dependency'leri düzgün handle ediyordu
- Daha iyi version resolution sağlıyordu
- Build tool'larla integrate oluyordu
Standart Olarak ES6 Modülleri#
ES6 (ES2015) sonunda JavaScript'e native module sistemi verdi:
// math.js
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// app.js
import { add, multiply } from './math.js';
Bu, CommonJS'in temiz syntax'ını AMD'nin static analysis faydalarıyla sağladı.
Translation Layer Olarak Babel#
Babel, eski browser'larda modern JavaScript kullanmak için essential hale geldi:
// Modern kod yaz
const users = await fetch('/api/users').then(r => r.json());
const admins = users.filter(u => u.role === 'admin');
// Babel compatible kod'a transform eder
var users = fetch('/api/users').then(function(r) { return r.json(); });
var admins = users.filter(function(u) { return u.role === 'admin'; });
Build Standardı Olarak webpack#
Karmaşıklığına rağmen, webpack başka hiçbir tool'un çözemediği problemleri çözdüğü için de facto standart haline geldi:
- Universal module sistemi (CommonJS, AMD, ES6)
- Asset management (CSS, resimler, fontlar)
- Code splitting ve lazy loading
- Hot module replacement
- Production optimizasyonları (tree shaking, minification)
Daha Fazla İnovasyonu Sürükleyen Acı Noktaları#
2016'ya kadar, modern frontend tooling stack kurulmuştu, ama birkaç acı noktası kalıyordu:
Configuration Yorgunluğu#
Yeni bir proje kurmak birden fazla tool'u anlamayı gerektiriyordu:
- Bundling için webpack
- Transpilation için Babel
- Linting için ESLint
- Testing için Jest
- CSS processing için PostCSS
Tipik bir proje 6-8 configuration dosyası ve yüzlerce satır setup kodu vardı.
Build Performance#
Büyük webpack build'leri 30+ saniye sürebiliyordu, development'ı yavaşlatıyordu. Hot reloading development sırasında yardım ediyordu, ama production build'leri acı verici derecede yavaştı.
Bundle Size Optimizasyonu#
Bundle size'ları optimize etmek webpack internal'larını derinlemesine bilmeyi gerektiriyordu. Tree shaking, code splitting ve chunk optimization gibi konseptler karmaşık ve kötü dokümante edilmişti.
Tool Birlikte Çalışabilirliği#
Farklı tool'ları birlikte çalıştırmak çoğunlukla kırılgandı. Bir tool'un configuration'ındaki değişiklikler başka bir tool'un varsayımlarını bozabilirdi.
Bu problemler bir sonraki inovasyon dalgası için sahneyi hazırladı: 2017-2020'de ortaya çıkacak zero-config tool'lar, performance-odaklı bundler'lar ve framework-integrated tooling.
İleriye Bakış: Temel Kuruldu#
2016'ya kadar, frontend development transform olmuştu. Manuel dosya yönetiminden şunları yapabilen sofistike build pipeline'larına geçmiştik:
- Dependency'leri otomatik yönetmek
- Browser uyumluluğu için modern kodu transform etmek
- Production için asset'leri optimize etmek
- Development sırasında neredeyse anında feedback sağlamak
- Optimal loading performance için kodu split etmek
Tool'lar güçlüydü ama karmaşıktı. Bir sonraki evrim o karmaşıklığı gizlerken daha da iyi performance ve developer experience sağlamaya odaklanacaktı.
Bu serinin bir sonraki bölümünde, Parcel, Vite ve esbuild gibi tool'ların performance ve karmaşıklık problemlerini nasıl ele aldığını, Next.js ve Vue CLI gibi framework'lerin manuel configuration'a nasıl opinionated alternatifler sağladığını ve native ES modülleri ile HTTP/2'nin ortaya çıkmasının bundling hakkındaki temel varsayımları nasıl değiştirdiğini keşfedeceğiz.
Devrim daha yeni başlıyordu.
Frontend Tooling'in Evrimi: Senior Mühendis Perspektifi
jQuery dosya birleştirmesinden Rust-powered bundler'lara - frontend tooling'in gerçek production problemlerini nasıl çözmek için evrildiğinin anlatılmamış hikayesi, savaş hikayeleri ve pratik deneyimlerle.
Bu Serideki Tüm Yazılar
Yorumlar (0)
Sohbete katıl
Düşüncelerini paylaşmak ve toplulukla etkileşim kurmak için giriş yap
Henüz yorum yok
Bu yazı hakkında ilk düşüncelerini paylaşan sen ol!
Yorumlar (0)
Sohbete katıl
Düşüncelerini paylaşmak ve toplulukla etkileşim kurmak için giriş yap
Henüz yorum yok
Bu yazı hakkında ilk düşüncelerini paylaşan sen ol!