# Asset Management: CSS/JS Minification

TYPO3 v13'te CSS ve JavaScript dosyalarının sıkıştırması, birleştirilmesi ve optimizasyonunu kapsar.  
İki temel yaklaşım mevcuttur: **TypoScript** (statik kayıt) ve **AssetCollector API** (dinamik/PHP).

---

## Render Sırası

Sayfaya eklenen asset'lerin HTML'deki sırası:

```
<head>
  page.includeCSSLibs.forceOnTop
  page.includeCSSLibs
  page.includeCSS.forceOnTop
  page.includeCSS
  AssetCollector::addStyleSheet()   ← priority=true
  page.cssInline

  page.includeJSLibs.forceOnTop
  page.includeJSLibs
  page.includeJS.forceOnTop
  page.includeJS
  AssetCollector::addJavaScript()   ← priority=true
  page.jsInline
  AssetCollector::addInlineJavaScript() ← priority=true
</head>

<body>
  ...içerik...

  page.includeJSFooterlibs.forceOnTop
  page.includeJSFooterlibs
  page.includeJSFooter.forceOnTop
  page.includeJSFooter
  AssetCollector::addJavaScript()   ← priority=false (varsayılan)
  page.jsFooterInline
  AssetCollector::addInlineJavaScript() ← priority=false (varsayılan)
</body>
```

> **Not:** `config.moveJsFromHeaderToFooter = 1` ile `includeJS` footer'a taşınır,  
> ancak `AssetCollector` ile eklenenler bu ayardan **etkilenmez**.

---

## TypoScript ile Asset Yönetimi

### 1. CSS Dosyaları

```typoscript
page {
    # ─── CDN CSS Kütüphaneleri (head'de, sıkıştırma dışı) ─────────────────────
    includeCSSLibs {
        googleFonts = https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap
        googleFonts.external = 1                    # URL işleme atlanır
        googleFonts.excludeFromConcatenation = 1    # Birleştirme dışında tut
        googleFonts.disableCompression = 1          # Sıkıştırma yapma

        fontAwesome = https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css
        fontAwesome.external = 1
        fontAwesome.excludeFromConcatenation = 1
        fontAwesome.integrity = sha512-iecdLm...==  # SRI hash
        fontAwesome.crossorigin = anonymous
    }

    # ─── Yerel CSS (head'de, sıkıştırılabilir) ────────────────────────────────
    includeCSS {
        layout   = EXT:testproject/Resources/Public/Css/layout.css
        main     = EXT:testproject/Resources/Public/Css/main.css
        main.media = screen

        # Zaten sıkıştırılmış dosya → sıkıştırma yapma
        vendor_min = EXT:testproject/Resources/Public/Css/vendor.min.css
        vendor_min.disableCompression = 1
        vendor_min.excludeFromConcatenation = 1

        # En üste zorunlu taşı
        critical = EXT:testproject/Resources/Public/Css/critical.css
        critical.forceOnTop = 1
    }
}
```

### 2. JavaScript Dosyaları

```typoscript
page {
    # ─── JS Kütüphaneleri (footer, CDN) ──────────────────────────────────────
    includeJSFooterlibs {
        jquery = https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js
        jquery.external = 1
        jquery.disableCompression = 1
        jquery.excludeFromConcatenation = 1
        jquery.integrity = sha512-3gJwYp...==
        jquery.crossorigin = anonymous
    }

    # ─── Yerel JS (footer, defer ile) ────────────────────────────────────────
    includeJSFooter {
        app = EXT:testproject/Resources/Public/JavaScript/main.js
        app.defer = 1

        # Build output (document root göreceli)
        assets_app = assets/js/app.js
        assets_app.defer = 1

        # Zaten sıkıştırılmış
        slider_min = EXT:testproject/Resources/Public/JavaScript/slider.min.js
        slider_min.disableCompression = 1
        slider_min.excludeFromConcatenation = 1
    }

    # ─── JS (head'de yüklenmesi gereken — kritik scriptler) ───────────────────
    includeJS {
        detection = EXT:testproject/Resources/Public/JavaScript/detection.js
        detection.async = 1
    }
}
```

### 3. Inline CSS/JS

```typoscript
page {
    cssInline {
        critical_inline = TEXT
        critical_inline.value = :root { --primary: #005a8e; }
    }

    jsInline {
        gtm_init = TEXT
        gtm_init.value = window.dataLayer = window.dataLayer || [];
    }

    jsFooterInline {
        app_config = TEXT
        app_config.value = window.siteConfig = {baseUrl: '/'};
    }
}
```

---

## Sıkıştırma (Compression) Ayarları

### config.typoscript — Proje Yapılandırması

```typoscript
# packages/testproject/Configuration/Sets/SitePackage/TypoScript/config.typoscript
config {
    # Sürüm 0 = kapalı (development varsayılanı)
    compressJs  = 0
    compressCss = 0
    concatenateJs  = 0
    concatenateCss = 0

    removeDefaultJS     = external   # TYPO3'ün eklediği varsayılan JS'i kaldır
    disablePrefixComment = 1         # HTML yorumlarını kaldır (küçültme)
    inlineStyle2TempFile = 1         # Inline style'ları geçici dosyaya aktar
}
```

### Ortam Bazlı Yapılandırma

```typoscript
# Production: tüm sıkıştırma ve birleştirme aktif
[applicationContext == "Production"]
    config.compressCss     = 1
    config.compressJs      = 1
    config.concatenateCss  = 1
    config.concatenateJs   = 1
[END]

# Development: sıkıştırma kapalı — tarayıcı debug'u kolaylaştırır
[applicationContext == "Development"]
    config.compressCss     = 0
    config.compressJs      = 0
    config.concatenateCss  = 0
    config.concatenateJs   = 0
[END]
```

### Tek Dosya için Hariç Tutma

| Seçenek | Açıklama |
|---------|----------|
| `disableCompression = 1` | Bu dosyayı sıkıştırma |
| `excludeFromConcatenation = 1` | Bu dosyayı birleştirme havuzuna alma |
| `external = 1` | URL'yi TYPO3 üzerinden işleme (CDN adresleri için) |
| `forceOnTop = 1` | Sayfanın en üstüne taşı |
| `defer = 1` | `<script defer>` ekle |
| `async = 1` | `<script async>` ekle |

> **Kural:** `.min.css` / `.min.js` uzantılı zaten-sıkıştırılmış dosyalara  
> mutlaka `disableCompression = 1` + `excludeFromConcatenation = 1` ekleyin.

---

## AssetCollector API (PHP)

PHP veya Fluid'den dinamik asset eklemek için `AssetCollector` kullanılır.  
Aynı `identifier` ile birden fazla kez çağrılırsa **yalnızca ilk kayıt** geçerlidir (deduplication).

### Doğru API İmzası

```php
// CSS
addStyleSheet(
    string $identifier,    // Benzersiz tanımlayıcı
    string $source,        // EXT: yolu veya URL
    array  $attributes,    // <link> tag HTML özellikleri
    array  $options        // ['priority' => bool]
)

// JS
addJavaScript(
    string $identifier,
    string $source,
    array  $attributes,    // ['defer' => 'defer', 'async' => 'async', 'type' => 'module', ...]
    array  $options        // ['priority' => bool]
)

// Inline CSS
addInlineStyleSheet(string $identifier, string $source, array $attributes, array $options)

// Inline JS
addInlineJavaScript(string $identifier, string $source, array $attributes, array $options)
```

### PHP Örneği (Constructor Injection)

```php
<?php

declare(strict_types=1);

namespace Gfdhfhgfgg\Testproject\DataProcessing;

use TYPO3\CMS\Core\Page\AssetCollector;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\DataProcessing\DataProcessorInterface;

final class CustomDataProcessor implements DataProcessorInterface
{
    public function __construct(
        private readonly AssetCollector $assetCollector,
    ) {}

    public function process(
        ContentObjectRenderer $cObj,
        array $contentObjectConfiguration,
        array $processorConfiguration,
        array $processedData
    ): array {
        // CSS — head'de yükle (priority=true)
        $this->assetCollector->addStyleSheet(
            'testproject-slider',
            'EXT:testproject/Resources/Public/Css/slider.css',
            ['media' => 'screen'],
            ['priority' => true]
        );

        // JS — footer'da defer ile yükle (priority=false, varsayılan)
        $this->assetCollector->addJavaScript(
            'testproject-slider-js',
            'EXT:testproject/Resources/Public/JavaScript/slider.js',
            ['defer' => 'defer'],
            ['priority' => false]
        );

        // ES Module
        $this->assetCollector->addJavaScript(
            'testproject-module',
            'EXT:testproject/Resources/Public/JavaScript/app.mjs',
            ['type' => 'module'],
            []
        );

        // Inline JS (JSON config gibi dinamik veri)
        $config = json_encode(['baseUrl' => '/', 'lang' => 'tr']);
        $this->assetCollector->addInlineJavaScript(
            'testproject-config',
            "window.SiteConfig = {$config};",
            [],
            ['priority' => true]
        );

        return $processedData;
    }
}
```

### Varlık Kontrolü

```php
// Zaten eklenmiş mi kontrol et
if (!$this->assetCollector->hasStyleSheet('testproject-slider')) {
    $this->assetCollector->addStyleSheet(...);
}

if (!$this->assetCollector->hasJavaScript('testproject-slider-js')) {
    $this->assetCollector->addJavaScript(...);
}
```

---

## Fluid ViewHelper ile Asset Ekleme

```html
{namespace core = TYPO3\CMS\Core\ViewHelpers}

<!-- CSS -->
<core:asset.css identifier="page-styles"
                href="{f:uri.resource(path:'Public/Css/page.css', extensionName:'testproject')}"
                media="screen"
                priority="false" />

<!-- JS (defer) -->
<core:asset.script identifier="page-scripts"
                   src="{f:uri.resource(path:'Public/JavaScript/main.js', extensionName:'testproject')}"
                   defer="defer"
                   priority="false" />

<!-- Inline JS -->
<core:asset.inlineScript identifier="page-config" priority="true">
    window.pageUid = {data.uid};
</core:asset.inlineScript>
```

---

## Proje Yapılandırması (testproject)

### Mevcut page.typoscript Özeti

```
HEAD (<head>)
├── includeCSSLibs
│   ├── Google Fonts Roboto Slab   (external, CDN)
│   ├── Google Fonts Roboto        (external, CDN)
│   ├── Google Fonts Roboto Mono   (external, CDN)
│   ├── Font Awesome 6.4.0         (external, CDN, integrity)
│   └── Swiper CSS 9               (external, CDN)
└── includeCSS
    ├── testproject_layout         (EXT:testproject/Css/layout.css)
    ├── assets_output              (assets/css/output.css — build)
    └── default_output             (EXT:testproject/Css/default-output.css, forceOnTop)

FOOTER (</body>)
├── includeJSFooterlibs
│   ├── jQuery 3.7.0               (external, CDN, integrity)
│   └── Swiper JS 9                (external, CDN)
└── includeJSFooter
    ├── testproject_scripts        (EXT:testproject/JavaScript/main.js)
    └── assets_app                 (assets/js/app.js — build, defer)
```

### Sıkıştırma Durumu

```typoscript
# config.typoscript'te şu an:
compressJs  = 0    # → Production'da 1 yapılmalı
compressCss = 0    # → Production'da 1 yapılmalı
concatenateJs  = 0
concatenateCss = 0
```

---

## Harici Build Araçları (Vite)

Daha ileri CSS/JS optimizasyonu için build süreci:

```bash
# Kurulum
npm init -y
npm install --save-dev vite
```

```javascript
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
    build: {
        outDir: 'public/assets',      // TYPO3 document root göreceli
        emptyOutDir: false,
        rollupOptions: {
            input: {
                app:    'src/js/main.js',
                styles: 'src/css/styles.css',
            },
            output: {
                entryFileNames: 'js/[name].js',
                assetFileNames: 'css/[name].[ext]',
            },
        },
        minify: 'terser',
        cssMinify: true,
    },
});
```

```bash
# Build
npm run build

# Watch modu (development)
npm run dev
```

TypoScript'te build çıktısını kullan:
```typoscript
page.includeCSS.assets_output = assets/css/styles.css
page.includeCSS.assets_output.media = screen
page.includeJSFooter.assets_app = assets/js/app.js
page.includeJSFooter.assets_app.defer = 1
```

---

## Performans Kontrol Listesi

- [ ] Production ortamında `compressCss = 1` ve `compressJs = 1`
- [ ] CDN dosyaları `external = 1` + `excludeFromConcatenation = 1`
- [ ] `.min.css` / `.min.js` dosyaları `disableCompression = 1`
- [ ] Font/icon CDN'leri için SRI (`integrity`) ve `crossorigin = anonymous` eklendi
- [ ] JS dosyaları footer'a taşındı (`includeJSFooter`)
- [ ] Uygun scriptlere `defer = 1` veya `async = 1` eklendi
- [ ] `config.removeDefaultJS = external` ile TYPO3 varsayılan JS kaldırıldı
- [ ] Gzip/Brotli sıkıştırma web sunucusunda aktif (nginx/Apache)
- [ ] Browser cache header'ları doğru (`sendCacheHeaders = 1`)

---

## Kaynaklar

- [TYPO3 Asset Management](https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Assets/)
- [AssetCollector API](https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Assets/Index.html)
- [TypoScript config Reference](https://docs.typo3.org/m/typo3/reference-typoscript/main/en-us/Setup/Config/)
- [TypoScript page Reference](https://docs.typo3.org/m/typo3/reference-typoscript/main/en-us/TopLevelObjects/Page/)
