DevTools में सीएसएस इन्फ़्रास्ट्रक्चर को आधुनिक बनाना

DevTools के आर्किटेक्चर को रीफ़्रेश करना: DevTools में सीएसएस इन्फ़्रास्ट्रक्चर को आधुनिक बनाना

यह पोस्ट, ब्लॉग पोस्ट की एक सीरीज़ का हिस्सा है. इसमें, DevTools के आर्किटेक्चर में किए जा रहे बदलावों और इसे बनाने के तरीके के बारे में बताया गया है. हम आपको बताएंगे कि DevTools में सीएसएस कैसे काम करती है. साथ ही, हमने किस तरह से JavaScript फ़ाइलों में सीएसएस लोड करने के लिए, वेब के स्टैंडर्ड समाधान पर माइग्रेट करने के लिए (आखिर में) DevTools में अपने सीएसएस को आधुनिक बनाने के लिए क्या किया है.

DevTools में सीएसएस की पिछली स्थिति

DevTools ने सीएसएस को दो अलग-अलग तरीकों से लागू किया है: पहला, DevTools के लेगसी पार्ट में इस्तेमाल की जाने वाली सीएसएस फ़ाइलों के लिए और दूसरा, DevTools में इस्तेमाल किए जा रहे आधुनिक वेब कॉम्पोनेंट के लिए.

DevTools में सीएसएस लागू करने की सुविधा कई साल पहले तय की गई थी और अब यह पुरानी हो गई है. DevTools में module.json पैटर्न का इस्तेमाल किया जाता है. इन फ़ाइलों को हटाने में काफ़ी मेहनत की गई है. इन फ़ाइलों को हटाने में आखिरी रुकावट, resources सेक्शन है. इसका इस्तेमाल सीएसएस फ़ाइलों को लोड करने के लिए किया जाता है.

हम उन अलग-अलग संभावित समाधानों को खोजने में समय बिताना चाहते थे जो आने वाले समय में CSS मॉड्यूल स्क्रिप्ट में बदल सकते हैं. इसका मकसद, लेगसी सिस्टम की वजह से हुए तकनीकी क़र्ज़ को खत्म करना था. साथ ही, सीएसएस मॉड्यूल स्क्रिप्ट पर माइग्रेट करने की प्रोसेस को आसान बनाना था.

DevTools में मौजूद सभी सीएसएस फ़ाइलों को 'लेगसी' माना जाता था, क्योंकि उन्हें module.json फ़ाइल का इस्तेमाल करके लोड किया गया था. इस फ़ाइल को हटाने की प्रोसेस जारी है. सभी सीएसएस फ़ाइलों को resources में module.json फ़ाइल में, सीएसएस फ़ाइल की उसी डायरेक्ट्री में लिस्ट किया जाना चाहिए.

बची हुई module.json फ़ाइल का उदाहरण:

{
  "resources": [
    "serviceWorkersView.css",
    "serviceWorkerUpdateCycleView.css"
  ]
}

इसके बाद, ये सीएसएस फ़ाइलें Root.Runtime.cachedResources नाम के ग्लोबल ऑब्जेक्ट मैप को पॉप्युलेट करेंगी. यह मैप, पाथ से उनके कॉन्टेंट तक की मैपिंग के तौर पर काम करेगा. DevTools में स्टाइल जोड़ने के लिए, आपको registerRequiredCSS को उस फ़ाइल के सटीक पाथ के साथ कॉल करना होगा जिसे लोड करना है.

registerRequiredCSS कॉल का उदाहरण:

constructor() {
  
  this.registerRequiredCSS('ui/legacy/components/quick_open/filteredListWidget.css');
  
}

इससे सीएसएस फ़ाइल का कॉन्टेंट वापस मिल जाएगा और appendStyle फ़ंक्शन का इस्तेमाल करके, उसे पेज में <style> एलिमेंट के तौर पर डाला जाएगा:.

appendStyle फ़ंक्शन, जो इनलाइन स्टाइल एलिमेंट का इस्तेमाल करके सीएसएस जोड़ता है:

const content = Root.Runtime.cachedResources.get(cssFile) || '';

if (!content) {
  console.error(cssFile + ' not preloaded. Check module.json');
}

const styleElement = document.createElement('style');
styleElement.textContent = content;
node.appendChild(styleElement);

जब हमने कस्टम एलिमेंट का इस्तेमाल करके, मॉडर्न वेब कॉम्पोनेंट को लॉन्च किया था, तब हमने शुरुआत में कॉम्पोनेंट फ़ाइलों में इनलाइन <style> टैग के ज़रिए सीएसएस का इस्तेमाल करने का फ़ैसला लिया था. इसमें कुछ चुनौतियां भी थीं:

  • सिंटैक्स हाइलाइट करने की सुविधा में कमी है. इनलाइन सीएसएस के लिए सिंटैक्स हाइलाइट करने की सुविधा देने वाले प्लग इन, .css फ़ाइलों में लिखी गई सीएसएस के लिए सिंटैक्स हाइलाइट करने और ऑटोकंप्लीट करने की सुविधाओं के मुकाबले उतने अच्छे नहीं होते.
  • परफ़ॉर्मेंस में आने वाले खर्च को कम करें. इनलाइन सीएसएस का मतलब यह भी है कि लिंटिंग के लिए दो पास होने चाहिए: एक सीएसएस फ़ाइलों के लिए और एक इनलाइन सीएसएस के लिए. यह परफ़ॉर्मेंस पर असर डालने वाला एक ओवरहेड था. अगर सभी सीएसएस, स्टैंडअलोन सीएसएस फ़ाइलों में लिखी जातीं, तो हम इसे हटा सकते थे.
  • छोटा करने में आ रही समस्या. इनलाइन सीएसएस को आसानी से छोटा नहीं किया जा सका, इसलिए किसी भी सीएसएस को छोटा नहीं किया गया. DevTools की रिलीज़ बिल्ड का फ़ाइल साइज़ भी बढ़ा दिया गया था. ऐसा एक ही वेब कॉम्पोनेंट के कई इंस्टेंस की वजह से शुरू हुए डुप्लीकेट सीएसएस की वजह से हुआ था.

मेरे इंटर्नशिप प्रोजेक्ट का मकसद, ऐसे सीएसएस इन्फ़्रास्ट्रक्चर के लिए एक समाधान खोजना था जो DevTools में इस्तेमाल किए जा रहे लेगसी इन्फ़्रास्ट्रक्चर और नए वेब कॉम्पोनेंट, दोनों के साथ काम करे.

संभावित समाधानों के बारे में रिसर्च करना

इस समस्या को दो अलग-अलग हिस्सों में बांटा जा सकता है:

  • यह पता लगाना कि बिल्ड सिस्टम, सीएसएस फ़ाइलों को कैसे मैनेज करता है.
  • पता लगाना कि DevTools की मदद से, सीएसएस फ़ाइलें कैसे इंपोर्ट की जाती हैं और उनका इस्तेमाल कैसे किया जाता है.

हमने हर हिस्से के लिए अलग-अलग संभावित समाधानों पर विचार किया है. इनके बारे में यहां बताया गया है.

सीएसएस फ़ाइलें इंपोर्ट करना

TypeScript फ़ाइलों में सीएसएस को इंपोर्ट और इस्तेमाल करने का मकसद, वेब स्टैंडर्ड के मुताबिक काम करना था. साथ ही, हमारे एचटीएमएल में, DevTools में एक जैसी सुविधाएं उपलब्ध कराना और डुप्लीकेट सीएसएस से बचना था. हम एक ऐसा समाधान भी चुनना चाहते थे जिससे हमारे बदलावों को वेब प्लैटफ़ॉर्म के नए स्टैंडर्ड, जैसे कि सीएसएस मॉड्यूल स्क्रिप्ट पर माइग्रेट किया जा सके.

इन वजहों से, @import स्टेटमेंट और टैग, DevTools के लिए सही नहीं लगते थे. ये, DevTools के बाकी हिस्सों में इंपोर्ट किए गए कॉन्टेंट से मेल नहीं खाएंगे. इस वजह से, बिना स्टाइल वाले कॉन्टेंट का फ़्लैश (FOUC) दिखेगा. सीएसएस मॉड्यूल स्क्रिप्ट पर माइग्रेट करना मुश्किल होगा, क्योंकि इंपोर्ट को साफ़ तौर पर जोड़ना होगा और <link> टैग के मुकाबले अलग तरीके से उनका इस्तेमाल करना होगा.

const output = LitHtml.html`
<style> @import "css/styles.css"; </style>
<button> Hello world </button>`
const output = LitHtml.html`
<link rel="stylesheet" href="styles.css">
<button> Hello World </button>`

@import या <link> का इस्तेमाल करके, संभावित समाधान.

इसके बजाय, हमने सीएसएस फ़ाइल को CSSStyleSheet ऑब्जेक्ट के तौर पर इंपोर्ट करने का तरीका ढूंढा, ताकि हम शैडो DOM (DevTools, पिछले कुछ सालों से शैडो DOM का इस्तेमाल करता है) में इसकी adoptedStyleSheets प्रॉपर्टी का इस्तेमाल करके, इसे जोड़ सकें.

बंडलर के विकल्प

हमें सीएसएस फ़ाइलों को CSSStyleSheet ऑब्जेक्ट में बदलने का तरीका चाहिए था, ताकि हम TypeScript फ़ाइल में आसानी से बदलाव कर सकें. यह काम पूरा करने के लिए, हमने रोलअप और वेबपैक, दोनों को संभावित बंडलर माना. DevTools, अपने प्रोडक्शन बिल्ड में पहले से ही Rollup का इस्तेमाल करता है. हालांकि, प्रोडक्शन बिल्ड में किसी भी बंडलर को जोड़ने पर, हमारे मौजूदा बिल्ड सिस्टम के साथ काम करते समय परफ़ॉर्मेंस से जुड़ी समस्याएं हो सकती हैं. Chromium के GN बिल्ड सिस्टम के साथ इंटिग्रेशन करने से, बंडल करना ज़्यादा मुश्किल हो जाता है. इसलिए, बंडलर, Chromium के मौजूदा बिल्ड सिस्टम के साथ अच्छी तरह से इंटिग्रेट नहीं होते.

इसके बजाय, हमने GN के मौजूदा बिल्ड सिस्टम का इस्तेमाल करने का विकल्प चुना, ताकि यह बदलाव हमारे लिए किया जा सके.

DevTools में सीएसएस का इस्तेमाल करने के लिए नया इन्फ़्रास्ट्रक्चर

नए समाधान में, किसी खास शैडो DOM में स्टाइल जोड़ने के लिए adoptedStyleSheets का इस्तेमाल किया जाता है. साथ ही, GN बिल्ड सिस्टम का इस्तेमाल करके CSSStyleSheet ऑब्जेक्ट जनरेट किए जाते हैं, जिन्हें document या ShadowRoot अपना सकता है.

// CustomButton.ts

// Import the CSS style sheet contents from a JS file generated from CSS
import customButtonStyles from './customButton.css.js';
import otherStyles from './otherStyles.css.js';

export class CustomButton extends HTMLElement{
  
  connectedCallback(): void {
    // Add the styles to the shadow root scope
    this.shadow.adoptedStyleSheets = [customButtonStyles, otherStyles];
  }
}

adoptedStyleSheets का इस्तेमाल करने के कई फ़ायदे हैं, जिनमें ये शामिल हैं:

  • यह एक मॉडर्न वेब स्टैंडर्ड बनने की प्रोसेस में है
  • डुप्लीकेट सीएसएस को रोकता है
  • सिर्फ़ शैडो DOM पर स्टाइल लागू करता है. इससे, सीएसएस फ़ाइलों में डुप्लीकेट क्लास के नाम या आईडी सिलेक्टर की वजह से होने वाली किसी भी समस्या से बचा जा सकता है
  • आने वाले समय में वेब के स्टैंडर्ड, जैसे कि सीएसएस मॉड्यूल स्क्रिप्ट और इंपोर्ट एश्योरेशन पर आसानी से माइग्रेट किया जा सकता है

इस समस्या को हल करने के लिए सिर्फ़ यह ज़रूरी था कि import स्टेटमेंट में .css.js फ़ाइल को इंपोर्ट करना पड़े. GN को बिल्डिंग के दौरान सीएसएस फ़ाइल जनरेट करने की अनुमति देने के लिए, हमने generate_css_js_files.js स्क्रिप्ट लिखी है. बिल्ड सिस्टम अब हर सीएसएस फ़ाइल को प्रोसेस करता है और उसे एक JavaScript फ़ाइल में बदल देता है. यह फ़ाइल डिफ़ॉल्ट रूप से CSSStyleSheet ऑब्जेक्ट एक्सपोर्ट करती है. यह बहुत अच्छा है, क्योंकि हम सीएसएस फ़ाइल को इंपोर्ट करके उसे आसानी से अपना सकते हैं. इसके अलावा, अब हम प्रोडक्शन बिल्ड को आसानी से छोटा कर सकते हैं, ताकि फ़ाइल का साइज़ कम हो सके:

const styles = new CSSStyleSheet();
styles.replaceSync(
  // In production, we also minify our CSS styles
  /`${isDebug ? output : cleanCSS.minify(output).styles}
  /*# sourceURL=${fileName} */`/
);

export default styles;

उदाहरण के लिए, स्क्रिप्ट से iconButton.css.js जनरेट किया गया.

ESLint नियमों का इस्तेमाल करके, लेगसी कोड को माइग्रेट करना

वेब कॉम्पोनेंट को मैन्युअल तरीके से आसानी से माइग्रेट किया जा सकता था, लेकिन registerRequiredCSS के लेगसी इस्तेमाल को माइग्रेट करने की प्रोसेस ज़्यादा मुश्किल थी. लेगसी स्टाइल को रजिस्टर करने वाले दो मुख्य फ़ंक्शन registerRequiredCSS और createShadowRootWithCoreStyles थे. हमने फ़ैसला लिया कि इन कॉल को माइग्रेट करने की प्रोसेस पूरी तरह से मकैनिकल थी. इसलिए, हम सुधार लागू करने और लेगसी कोड को अपने-आप माइग्रेट करने के लिए, ESLint नियमों का इस्तेमाल कर सकते हैं. DevTools, DevTools कोडबेस के लिए पहले से ही कई कस्टम नियमों का इस्तेमाल करता है. यह मददगार था, क्योंकि ESLint पहले से ही कोड को एब्स्ट्रैक्ट सिंटैक्स ट्री(संक्षिप्त रूप में, AST) के बारे में जानकारी मिलती है. साथ ही, हम उन कॉल नोड के बारे में क्वेरी कर सकते हैं जो सीएसएस को रजिस्टर करने के लिए किए गए कॉल थे.

माइग्रेशन के लिए ESLint नियम लिखते समय, हमें सबसे बड़ी समस्या एज केस कैप्चर करने में आई. हम यह पक्का करना चाहते थे कि हम यह तय कर पाएं कि किन एज केस को कैप्चर करना ज़रूरी है और किन को मैन्युअल तरीके से माइग्रेट करना चाहिए. हम यह भी पक्का करना चाहते थे कि अगर इंपोर्ट की गई .css.js फ़ाइल को बिल्ड सिस्टम से अपने-आप जनरेट नहीं किया जा रहा है, तो हम उपयोगकर्ता को इस बारे में बता सकें, क्योंकि इससे रनटाइम के दौरान किसी भी फ़ाइल नहीं मिलने पर होने वाली गड़बड़ी से बचा जा सकता है.

माइग्रेशन के लिए ESLint नियमों का इस्तेमाल करने का एक नुकसान यह था कि हम सिस्टम में ज़रूरी GN बिल्ड फ़ाइल को नहीं बदल पाए. ये बदलाव हर डायरेक्ट्री में, उपयोगकर्ता को मैन्युअल तौर पर करने थे. हालांकि, इसके लिए ज़्यादा काम करना पड़ता था, लेकिन यह पुष्टि करने का एक अच्छा तरीका था कि इंपोर्ट की जा रही हर .css.js फ़ाइल, असल में बिल्ड सिस्टम से जनरेट की गई है.

कुल मिलाकर, इस माइग्रेशन के लिए ESLint के नियमों का इस्तेमाल करना काफ़ी मददगार रहा. इसकी मदद से, हमने लेगसी कोड को नए इन्फ़्रास्ट्रक्चर पर तेज़ी से माइग्रेट किया. साथ ही, एएसटी के तुरंत उपलब्ध होने का मतलब है कि हम नियम में कई एज केस भी मैनेज कर सकते हैं. साथ ही, ESLint के फ़िक्सर एपीआई का इस्तेमाल करके, उन्हें अपने-आप ठीक कर सकते हैं.

आगे क्या?

फ़िलहाल, Chromium DevTools में मौजूद सभी वेब कॉम्पोनेंट को माइग्रेट कर दिया गया है, ताकि इनलाइन स्टाइल के बजाय नए सीएसएस इन्फ़्रास्ट्रक्चर का इस्तेमाल किया जा सके. नए सिस्टम का इस्तेमाल करने के लिए, registerRequiredCSS के ज़्यादातर लेगसी इस्तेमाल को भी माइग्रेट कर दिया गया है. अब आपको ज़्यादा से ज़्यादा module.json फ़ाइलें हटानी हैं. इसके बाद, आने वाले समय में सीएसएस मॉड्यूल स्क्रिप्ट लागू करने के लिए, इस मौजूदा इन्फ़्रास्ट्रक्चर को माइग्रेट करना है!

झलक वाले चैनल डाउनलोड करना

Chrome कैनरी, डेवलपर या बीटा को अपने डिफ़ॉल्ट डेवलपमेंट ब्राउज़र के तौर पर इस्तेमाल करें. इन झलक वाले चैनलों की मदद से, आपको DevTools की नई सुविधाओं का ऐक्सेस मिलता है. साथ ही, इनसे आपको वेब प्लैटफ़ॉर्म के सबसे नए एपीआई की जांच करने में मदद मिलती है. इसके अलावा, इनकी मदद से उपयोगकर्ताओं से पहले ही अपनी साइट पर समस्याओं का पता लगाया जा सकता है!

Chrome DevTools की टीम से संपर्क करें

DevTools से जुड़ी नई सुविधाओं, अपडेट या किसी भी अन्य चीज़ के बारे में चर्चा करने के लिए, यहां दिए गए विकल्पों का इस्तेमाल करें.