Normal ifadelerin ötesinde: Chrome Geliştirici Araçları'nda CSS değeri ayrıştırmasını geliştirme

Philip Pfaffe
Ergün Erdogmus
Ergün Erdogmus

Chrome Geliştirici Araçları'ndaki CSS özelliklerini fark ettiniz mi? Stiller sekmesi son zamanlarda biraz daha gösterişli görünüyor mu? Chrome 121 ile 128 sürümleri arasında kullanıma sunulan bu güncellemeler, CSS değerlerini ayrıştırma ve sunma şeklimizdeki önemli bir iyileştirmenin sonucudur. Bu makalede, normal ifadelerle eşleşen sistemden daha sağlam bir ayrıştırıcıya geçiş yaparak bu dönüşümün teknik ayrıntıları konusunda size yol göstereceğiz.

Mevcut Geliştirici Araçları'nı önceki sürümle karşılaştıralım:

Üst: Chrome en son sürümü, Alt: Chrome 121.

Oldukça önemli, değil mi? Temel geliştirmelerin dökümü aşağıda verilmiştir:

  • color-mix color-mix işlevindeki iki renk bağımsız değişkenini görsel olarak temsil eden kullanışlı bir önizleme.
  • pink Adlandırılmış pink rengi için tıklanabilir bir renk önizlemesi. Kolay ayarlamalar yapmak üzere bir renk seçici açmak için bu simgeyi tıklayın.
  • var(--undefined, [fallback value]) Tanımlanmamış değişken gri renkte ve tıklanabilir renk önizlemesiyle birlikte etkin yedek değer (bu durumda HSL rengi) görüntülenerek tanımlanmamış değişkenlerin işlenmesi iyileştirildi.
  • hsl(…): hsl renk işlevi için, renk seçiciye hızlı erişim sağlayan başka bir tıklanabilir renk önizlemesi.
  • 177deg: Etkileşimli olarak sürükleyip açı değerini değiştirmenize olanak tanıyan, tıklanabilir bir açı saat.
  • var(--saturation, …): Özel mülk tanımına ait, ilgili beyana kolayca geçilmesini sağlayan tıklanabilir bağlantı.

Aralarındaki fark çarpıcıdır. Bunu başarmak için Geliştirici Araçları'na CSS mülk değerlerini eskisinden çok daha iyi anlamayı öğretmemiz gerekiyordu.

Bu önizlemeler zaten kullanılabilir değil miydi?

Bu önizleme simgeleri tanıdık gelebilir, ancak özellikle yukarıdaki örnek gibi karmaşık CSS söz diziminde her zaman tutarlı bir şekilde gösterilmemiştir. Çalıştıkları durumlarda bile, doğru şekilde işlemeleri için çoğu zaman çok çaba göstermeleri gerekiyordu.

Bunun nedeni, değer analizi sisteminin Geliştirici Araçları'nın ilk günlerinden bu yana organik olarak büyümesidir. Ancak CSS'den edindiğimiz son muhteşem yeni özelliklere ve bunun sonucunda dilin karmaşıklığındaki artışa ayak uyduramadı. Sistem, evrime ayak uydurabilmek için baştan sona yeni bir tasarıma ihtiyaç duydu. Biz de bunu yaptık.

CSS mülk değerleri nasıl işlenir?

Geliştirici Araçları'nda, Stiller sekmesindeki mülk bildirimlerini oluşturma ve süsleme işlemi iki farklı aşamadan oluşur:

  1. Yapısal analiz. Bu ilk aşamada, temel bileşenleri ve bunların ilişkilerini belirlemek için mülk beyanı incelenir. Örneğin, border: 1px solid red bildiriminde, uzunluk olarak 1px, dize olarak solid ve renk olarak red tanınır.
  2. Oluşturma. Oluşturma aşamasında, yapısal analiz temel alınarak bu bileşenler bir HTML gösterimine dönüştürülür. Bu, görüntülenen özellik metnini etkileşimli öğeler ve görsel ipuçlarıyla zenginleştirir. Örneğin, red renk değeri, tıklandığında kolayca değişiklik yapabilmeniz için bir renk seçici gösteren tıklanabilir bir renk simgesiyle oluşturulur.

Normal ifadeler

Daha önce, yapısal analizde özellik değerlerini incelemek için normal ifadelerden (normal ifadeler) yararlanıyorduk. Dekorasyon yapmayı düşündüğümüz özellik değerlerinin parçalarına uygun bir normal ifade listesi hazırladık. Örneğin, CSS renkleri, uzunlukları, açılar ve var işlev çağrıları gibi daha karmaşık alt ifadelerle eşleşen ifadeler vardı. Değer analizi yapmak için metni soldan sağa doğru taradık ve listede sürekli olarak metnin bir sonraki parçasıyla eşleşen ilk ifadeyi aradık.

Bu yaklaşım çoğu zaman iyi sonuç verdi ancak artışın devam etmediği durumların sayısı. Yıllar içinde çok sayıda hata raporu aldık. Ancak eşleştirme hatalı. Bazı düzeltmeleri basit, bazıları ise oldukça incelikli olan bu sorunları giderirken teknik borçlarımızı aşmak için yaklaşımımızı yeniden düşünmek zorunda kaldık. Sorunlardan bazılarına göz atalım.

Eşleşen color-mix()

color-mix() işlevi için kullandığımız normal ifade şuydu:

/color-mix\(.*,\s*(?<firstColor>.+)\s*,\s*(?<secondColor>.+)\s*\)/g

Söz dizimiyle eşleşen kullanıcı:

color-mix(<color-interpolation-method>, [<color> && <percentage [0,100]>?]#{2})

Eşleşmeleri görselleştirmek için aşağıdaki örneği çalıştırmayı deneyin.

const re = /color-mix\(.*,\s*(?<firstColor>.+)\s*,\s*(?<secondColor>.+)\s*\)/g;

// it works - simpler example
const simpler = re.exec('color-mix(in srgb, pink, hsl(127deg 100% 50%))');
console.table(simpler.groups);

re.exec('');

// it doesn't work - complex example
const complex = re.exec('color-mix(in srgb, pink, var(--undefined, hsl(127deg var(--saturation, 100%) 50%)))');
console.table(complex.groups);

Renk karması işlevi için eşleşme sonucu.

Sade örnek sorunsuz bir şekilde çalışır. Ancak daha karmaşık örnekte, <firstColor> eşleşmesi hsl(177deg var(--saturation ve <secondColor> eşleşmesi 100%) 50%)). Bu da tamamen anlamsızdır.

Bunun bir sorun olduğunu biliyorduk. Sonuçta, resmi dil olarak CSS düzenli değildir. Bu nedenle, var işlevleri gibi daha karmaşık işlev bağımsız değişkenleriyle başa çıkmak için özel işleme ekledik. Ancak, ilk ekran görüntüsünde görebileceğiniz gibi bu yöntem her durumda çalışmadı.

Eşleşen tan()

Bildirilen daha komik hatalardan biri, trigonometrik tan() işleviyle ilgiliydi . Renkleri eşleştirmek için kullandığımız normal ifade, red anahtar kelimesi gibi adlandırılmış renkleri eşleştirmek için bir alt ifade \b[a-zA-Z]+\b(?!-) içeriyordu. Daha sonra, eşleşen bölümün gerçekten adlandırılmış bir renk olup olmadığını kontrol ettik ve tan öğesinin de adlandırılmış bir renk olduğunu tahmin ettik! Bu nedenle tan() ifadeleri yanlış bir şekilde renkler olarak yorumladık.

Eşleşen var()

var() işlevlerinin, diğer var() referansları içeren bir yedek içeren başka bir örneği inceleyelim: var(--non-existent, var(--margin-vertical)).

var() için normal ifademiz bu değerle rahat bir şekilde eşleşir. Ancak ilk kapanış parantezinde eşleştirme durdurur. Bu nedenle, yukarıdaki metin var(--non-existent, var(--margin-vertical) olarak eşleştirilir. Bu, normal ifade eşleşmesi için bir ders kitabı sınırlamasıdır. Eşleşen parantez gerektiren diller temelde normal değildir.

CSS ayrıştırıcıya geçiş

Normal ifadeler kullanarak yapılan metin analizi çalışmadığında (analiz edilen dil normal olmadığı için) standart bir sonraki adım vardır: Daha yüksek türden bir dilbilgisi için ayrıştırıcı kullanma. CSS için bu, bağlamsız diller için ayrıştırıcı anlamına gelir. Aslında Dev Tools'un kod tabanında böyle bir ayrıştırıcı sistemi zaten vardı: CodeMirror'ın Lezer çözümü, örneğin Kaynaklar panelinde bulduğunuz düzenleyici olan CodeMirror'daki söz dizimi vurgulama özelliğinin temelini oluşturur. Lezer'ın CSS ayrıştırıcısı, CSS kuralları için (soyut olmayan) söz dizimi ağaçları üretmemizi sağladı ve bizim için kullanıma hazırdı. Zafer.

&quot;hsl(177deg var(--saturation, 100%) 50%)&quot; özellik değeri için söz dizimi ağacı. Lezer ayrıştırıcısı tarafından üretilen sonucun, virgül ve parantez için tamamen söz dizimsel düğümleri çıkarıp basitleştirilmiş bir sürümüdür.

Ancak, normal ifade tabanlı eşlemeden doğrudan ayrıştırıcı tabanlı eşlemeye geçiş yapmanın mümkün olmadığını gördük. İki yaklaşım da karşıt yönlerden işe yarıyor. Değer parçalarını normal ifadelerle eşleştirirken Geliştirici Araçları, girişi soldan sağa doğru tarayarak sıralı bir kalıp listesindeki en erken eşleşmeyi bulmaya çalışır. Söz dizimi ağacında eşleştirme, aşağıdan yukarıya doğru başlar (örneğin, işlev çağrısını eşleştirmeye çalışmadan önce ilk olarak bir çağrının bağımsız değişkenlerinin analiz edilmesi). Bunu, önce parantez içinde ifadeler, sonra çarpımsal operatörler ve toplama operatörleri üzerinde düşünebileceğiniz aritmetik bir ifadeyi değerlendirme olarak düşünebilirsiniz. Bu çerçevelemede, normal ifadeye dayalı eşleştirme, aritmetik ifadenin soldan sağa doğru değerlendirilmesine karşılık gelir. Aslında eşleştirme sisteminin tamamını sıfırdan yeniden yazmak istemedik: 15 farklı eşleştirici ve oluşturucu çifti ve içlerinde binlerce kod vardı. Bu da, eşleştirme sistemini tek bir ara hedefte göndermemizin pek olası olmadığını gösteriyordu.

Bu nedenle, kademeli değişiklikler yapmamıza olanak tanıyan bir çözüm bulduk. Bu çözümü, aşağıda daha ayrıntılı olarak açıklayacağız. Kısacası, iki aşamalı yaklaşımı koruduk, ancak ilk aşamada alt ifadeleri aşağıdan yukarıya eşleştirmeye çalışıyoruz (böylece normal ifade akışı bozuluyor) ve ikinci aşamada yukarıdan aşağıya yeniden oluşturuyoruz. Her iki aşamada da mevcut normal ifade tabanlı eşleştiricileri ve oluşturmaları neredeyse değiştirmeden kullanabilir ve böylece bunları tek tek taşıyabildik.

1. Aşama: Aşağıdan yukarıya eşleştirme

İlk aşama neredeyse tamamen ve sadece kapakta yazanları uyguluyor. Ağacı aşağıdan yukarıya doğru sırayla katediyoruz ve ziyaret ettiğimiz her söz dizimi ağacı düğümünde alt ifadeleri eşleştirmeye çalışıyoruz. Eşleştirici, belirli bir alt ifadeyi eşleştirmek için mevcut sistemde olduğu gibi normal ifadeyi kullanabilir. 128 sürümünde ise bazı durumlarda (ör. uzunlukları eşleştirmek) kullanmaya devam ediyoruz. Alternatif olarak, bir eşleştirici, mevcut düğümde kökü bulunan alt ağacın yapısını analiz edebilir. Böylece söz dizimi hatalarını yakalayıp yapısal bilgileri aynı anda kaydedebilir.

Yukarıdaki söz dizimi ağacı örneğini düşünün:

1. Aşama: Söz dizimi ağacında aşağıdan yukarıya eşleştirme.

Bu ağaç için eşleştiricilerimiz aşağıdaki sırayla uygulanır:

  1. hsl(177degvar(--saturation, 100%) 50%): İlk olarak, hsl işlev çağrısının ilk bağımsız değişkeni olan ton açısını keşfediyoruz. Açı değerini, açı simgesiyle süsleyebilmek için açı eşleştiriciyle eşleştiririz.
  2. hsl(177degvar(--saturation, 100%)50%): İkinci olarak, bir var eşleştirici ile var işlev çağrısını keşfediyoruz. Bu tür aramalar için temel olarak iki şey yapmak istiyoruz:
    • Değişkenin bildirimini arayın ve değerini hesaplayın. Ardından, değişken adına bağlanmak için değişken adına bir bağlantı ve açılır pencere ekleyin.
    • Hesaplanan değer bir renkse görüşmeyi bir renk simgesiyle süsleyin. Üçüncü bir şey daha var, ama bunu daha sonra ele alacağız.
  3. hsl(177deg var(--saturation, 100%) 50%): Son olarak, renk simgesiyle süsleyebilmek için hsl işlevine yönelik çağrı ifadesini eşleştiririz.

Süslemek istediğimiz alt ifadeleri aramaya ek olarak, eşleştirme süreci kapsamında kullandığımız ikinci bir özellik daha vardır. 2. adımda, bir değişken adı için hesaplanan değeri aradığımızı belirttiğimizi unutmayın. Hatta bu çözümü bir adım daha ileri götürüp sonuçları ağaçta daha yukarılara taşıyoruz. Ayrıca yalnızca değişken için değil, yedek değer için de. Bir var işlev düğümü ziyaret edildiğinde, alt öğelerinin önceden ziyaret edildiği garanti edilir. Bu nedenle, yedek değerde görünebilecek tüm var işlevlerinin sonuçlarını zaten biliyoruz. Bu nedenle, var işlevlerini kolay ve ucuz bir şekilde kendi sonuçlarıyla değiştirebiliriz. Bu da, 2. adımda yaptığımız gibi "Bu var işlevinin sonucu bir renk mi çağırır?" gibi soruları deneysel bir şekilde yanıtlamamızı sağlar.

2. Aşama: Yukarıdan aşağıya oluşturma

İkinci aşama için yönü tersine çeviririz. 1. aşamadaki eşleşme sonuçlarını aldığımızda, ağacı yukarıdan aşağıya sırayla geçiş yaparak HTML'ye dönüştürürüz. Ziyaret edilen her düğüm için eşleşip eşleşmediğini kontrol ederiz. Eşleşenyse, eşleştiricinin ilgili oluşturucusunu çağırırız. Metin düğümleri için bir varsayılan eşleştirici ve oluşturucu ekleyerek yalnızca metin (ör. NumberLiteral "%50") içeren düğümler için özel işleme ihtiyacını ortadan kaldırıyoruz. Oluşturucular yalnızca HTML düğümlerinin çıktısını verir ve bu düğümler bir araya getirildiğinde, süslemeleri de dahil olmak üzere özellik değerinin gösterimini oluşturur.

2. Aşama: Söz dizimi ağacında yukarıdan aşağıya oluşturma.

Örnek ağaç için özellik değerinin oluşturulma sırası şu şekildedir:

  1. hsl işlev çağrısını ziyaret edin. Eşleştiği için renk işlevi oluşturucusunu çağırın. İki şey yapar:
    • var bağımsız değişkenleri için anında değiştirme mekanizmasını kullanarak gerçek renk değerini hesaplar, ardından bir renk simgesi çizer.
    • CallExpression öğesinin alt öğelerini yinelemeli olarak oluşturur. Bu işlem, yalnızca metin olan işlev adını, parantezleri ve virgülleri otomatik olarak oluşturur.
  2. hsl çağrısının ilk bağımsız değişkenini ziyaret edin. Bu nedenle, açı simgesini ve açı metnini çeken açı oluşturucuyu çağırın.
  3. var çağrısı olan ikinci bağımsız değişkeni ziyaret edin. Eşleştiği için var renderer öğesini çağırın. Bu işlem şu sonucu verir:
    • Baştaki var( metni.
    • Değişkenin adı ve değişken tanımına yönlendiren bir bağlantıyla veya tanımsız olduğunu belirten gri bir metin rengiyle süsler. Ayrıca, değişken değeriyle ilgili bilgileri göstermek için değişkene bir pop-up ekler.
    • Virgül ve ardından, yedek değeri yinelemeli bir şekilde oluşturur.
    • Kapanış parantezi.
  4. hsl çağrısının son bağımsız değişkenini ziyaret edin. Eşleşmedi, bu nedenle metin içeriğini çıktı olarak alın.

Bu algoritmada, oluşturma işleminin eşleşen bir düğümün alt öğelerinin nasıl oluşturulduğunu tam olarak kontrol ettiğini fark ettiniz mi? Alt öğeleri yinelemeli olarak oluşturmak proaktif bir yaklaşımdır. Bu püf noktası, normal ifade tabanlı oluşturmadan söz dizimi ağacı tabanlı oluşturmaya adım adım geçişin gerçekleşmesini sağladı. Eski bir normal ifade eşleştiriciyle eşleşen düğümler için karşılık gelen oluşturucu orijinal biçiminde kullanılabilir. Söz dizimi ağacı terimleriyle, alt ağacın tamamını oluşturmaktan sorumlu olacaktı ve sonucu (bir HTML düğümü) etrafındaki oluşturma işlemine temiz bir şekilde eklenebilir. Bu sayede, eşleştiricileri ve oluşturucuları çiftler halinde taşıma ve tek tek değiştirme seçeneği sunduk.

Oluşturucuların eşleşen düğümlerinin alt öğelerinin oluşturulmasını kontrol eden bir başka harika özellik de, eklediğimiz simgeler arasındaki bağımlılıklar hakkında akıl yürütmemize imkan vermesidir. Yukarıdaki örnekte, hsl işlevi tarafından üretilen renk, açık bir şekilde ton değerine bağlıdır. Bu, renk simgesiyle gösterilen rengin, açı simgesiyle gösterilen açıya bağlı olduğu anlamına gelir. Kullanıcı bu simge aracılığıyla açı düzenleyiciyi açar ve açıyı değiştirirse, artık renk simgesinin rengini gerçek zamanlı olarak güncelleyebiliriz:

Yukarıdaki örnekte görebileceğiniz gibi bu mekanizmayı diğer simge eşlemeleri için de kullanıyoruz. Örneğin color-mix() ve iki renk kanalı ya da yedekten bir renk döndüren var işlevleri için kullanıyoruz.

Performansa olan etkisi

Güvenilirliği artırmak ve uzun süredir var olan sorunları düzeltmek amacıyla bu sorunu incelerken, tam kapsamlı bir ayrıştırıcı çalıştırmaya başladığımızı düşünerek bir miktar performans gerilemesi olmasını bekliyorduk. Bunu test etmek için yaklaşık 3, 5 bin özellik bildirimi oluşturan bir karşılaştırma oluşturduk ve bir M1 makinesinde 6 kat sınırlama ile hem normal ifade tabanlı hem de ayrıştırıcı tabanlı sürümlerin profilini çıkardık.

Beklendiğimiz gibi, ayrıştırmaya dayalı yaklaşım, bu durumda normal ifade temelli yaklaşıma göre% 27 daha yavaş sonuç verdi. Normal ifade tabanlı yaklaşımın oluşturulması 11 sn, ayrıştırıcı tabanlı yaklaşımın oluşturulması 15 saniye sürdü.

Yeni yaklaşımdan elde ettiğimiz kazanımları göz önünde bulundurarak bu yaklaşımda ilerlemeye karar verdik.

Teşekkür

Bu gönderiyi düzenleme konusunda verdikleri paha biçilmez yardımları için Sofia Emelianova ve Jecelyn Yeen'e minnettarız.

Önizleme kanallarını indirme

Varsayılan geliştirme tarayıcınız olarak Chrome Canary, Dev veya Beta'yı kullanabilirsiniz. Bu önizleme kanalları en yeni Geliştirici Araçları özelliklerine erişmenizi, son teknoloji ürünü web platformu API'lerini test etmenizi ve kullanıcılarınızdan önce sitenizdeki sorunları bulmanızı sağlar.

Chrome Geliştirici Araçları ekibiyle iletişim kurma

Yayındaki yeni özellikleri ve değişiklikleri ya da Geliştirici Araçları ile ilgili diğer her şeyi tartışmak için aşağıdaki seçenekleri kullanın.

  • Öneri veya geri bildirimlerinizi crbug.com adresinden bize iletebilirsiniz.
  • Geliştirici Araçları sorunlarını bildirmek için Diğer seçenekler'i Daha fazla > Yardım > Geliştirici Araçları'nda Geliştirici Araçları ile ilgili sorunları bildirin.
  • @ChromeDevTools adresinden tweet atabilirsiniz.
  • Geliştirici Araçları YouTube videoları veya Geliştirici Araçları ipuçları YouTube videolarına yorum yazın.