केस स्टडी: DevTools की मदद से बेहतर एंगुलर डीबगिंग

डीबग करने का बेहतर अनुभव

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

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

अनदेखा किए जाने वाले कोड की सूची

Chrome DevTools का इस्तेमाल करके ऐप्लिकेशन डीबग करते समय, लेखक आम तौर पर सिर्फ़ अपना कोड देखना चाहते हैं, न कि फ़्रेमवर्क या node_modules फ़ोल्डर में मौजूद किसी डिपेंडेंसी को.

इसे पूरा करने के लिए, DevTools की टीम ने सोर्स मैप के लिए एक एक्सटेंशन लॉन्च किया है. इसे x_google_ignoreList कहा जाता है. इस एक्सटेंशन का इस्तेमाल, तीसरे पक्ष के सोर्स की पहचान करने के लिए किया जाता है. जैसे, फ़्रेमवर्क कोड या बंडलर से जनरेट किया गया कोड. जब कोई फ़्रेमवर्क इस एक्सटेंशन का इस्तेमाल करता है, तो लेखक अब उस कोड से अपने-आप बच जाते हैं जिसे उन्हें नहीं देखना है या जिसे पहले से मैन्युअल तरीके से कॉन्फ़िगर किए बिना, सिलसिलेवार तरीके से नहीं देखना है.

असल में, Chrome DevTools, स्टैक ट्रेस, सोर्स ट्री, और तुरंत खोलने वाले डायलॉग में, इस तरह के कोड को अपने-आप छिपा सकता है. साथ ही, डीबगर में चरण और फिर से शुरू करने के व्यवहार को भी बेहतर बना सकता है.

ऐनिमेशन वाला GIF, जिसमें DevTools के पहले और बाद की इमेज दिखाई गई है. ध्यान दें कि बाद की इमेज में, DevTools ने ट्री में लिखा गया कोड कैसे दिखाया है. अब “तुरंत खोलें” मेन्यू में, फ़्रेमवर्क की किसी भी फ़ाइल का सुझाव नहीं दिया जाता है. साथ ही, दाईं ओर स्टैक ट्रेस को ज़्यादा साफ़ तौर पर दिखाया गया है.

x_google_ignoreList सोर्स मैप एक्सटेंशन

सोर्स मैप में, नया x_google_ignoreList फ़ील्ड sources कलेक्शन का रेफ़रंस देता है. साथ ही, उस सोर्स मैप में तीसरे पक्ष के सभी सोर्स के इंडेक्स की सूची दिखाता है. सोर्स मैप को पार्स करते समय, Chrome DevTools इसका इस्तेमाल करके यह पता लगाएगा कि कोड के किन सेक्शन को अनदेखा करने की सूची में शामिल किया जाना चाहिए.

जनरेट की गई फ़ाइल out.js का सोर्स मैप यहां दिया गया है. आउटपुट फ़ाइल जनरेट करने में दो ओरिजनल sources का योगदान है: foo.js और lib.js. पहला कोड, वेबसाइट डेवलपर ने लिखा है और दूसरा कोड, उसने इस्तेमाल किया है.

{
  "version" : 3,
  "file": "out.js",
  "sourceRoot": "",
  "sources": ["foo.js", "lib.js"],
  "sourcesContent": ["...", "..."],
  "names": ["src", "maps", "are", "fun"],
  "mappings": "A,AAAB;;ABCDE;"
}

sourcesContent इन दोनों ओरिजनल सोर्स के लिए शामिल किया गया है और Chrome DevTools, इन फ़ाइलों को डिफ़ॉल्ट रूप से डीबगर में दिखाएगा:

  • सोर्स ट्री में फ़ाइलों के तौर पर.
  • क्विक ओपन डायलॉग में नतीजों के तौर पर.
  • गड़बड़ी के स्टैक ट्रेस में, ब्रेकपॉइंट पर रोके जाने और सिलसिलेवार तरीके से आगे बढ़ने के दौरान, कॉल फ़्रेम की जगहों को मैप किया गया है.

सोर्स मैप में एक और जानकारी शामिल की जा सकती है. इससे यह पता चलता है कि उनमें से कौनसा सोर्स पहले या तीसरे पक्ष का कोड है:

{
  ...
  "sources": ["foo.js", "lib.js"],
  "x_google_ignoreList": [1],
  ...
}

नए x_google_ignoreList फ़ील्ड में, sources कलेक्शन का रेफ़रंस देने वाला एक इंडेक्स है: 1. इससे पता चलता है कि lib.js से मैप किए गए क्षेत्र, असल में तीसरे पक्ष के कोड हैं. इन्हें अनदेखा करने की सूची में अपने-आप जोड़ दिया जाना चाहिए.

नीचे दिए गए ज़्यादा जटिल उदाहरण में, इंडेक्स 2, 4, और 5 से पता चलता है कि lib1.ts, lib2.coffee, और hmr.js से मैप किए गए सभी क्षेत्र, तीसरे पक्ष के कोड हैं. इन्हें अनदेखा की जाने वाली सूची में अपने-आप जोड़ दिया जाना चाहिए.

{
  ...
  "sources": ["foo.html", "bar.css", "lib1.ts", "baz.js", "lib2.coffee", "hmr.js"],
  "x_google_ignoreList": [2, 4, 5],
  ...
}

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

Angular में x_google_ignoreList

Angular v14.1.0 से, node_modules और webpack फ़ोल्डर के कॉन्टेंट को “अनदेखा करें” के तौर पर मार्क किया गया है.

ऐसा angular-cli में बदलाव करके किया गया. इसके लिए, webpack के Compiler मॉड्यूल में हुक करने वाला प्लग इन बनाया गया

हमारे इंजीनियरों ने PROCESS_ASSETS_STAGE_DEV_TOOLING चरण में हुक बनाने के लिए, webpack प्लग इन बनाया है. यह प्लग इन, वेबपैक से जनरेट होने वाली और ब्राउज़र से लोड होने वाली फ़ाइनल ऐसेट के लिए, सोर्स मैप में x_google_ignoreList फ़ील्ड को पॉप्युलेट करता है.

const map = JSON.parse(mapContent) as SourceMap;
const ignoreList = [];

for (const [index, path] of map.sources.entries()) {
  if (path.includes('/node_modules/') || path.startsWith('webpack/')) {
    ignoreList.push(index);
  }
}

map[`x_google_ignoreList`] = ignoreList;
compilation.updateAsset(name, new RawSource(JSON.stringify(map)));

लिंक किए गए स्टैक ट्रेस

स्टैक ट्रेस से “मैं यहां कैसे पहुंचा” सवाल का जवाब मिलता है. हालांकि, अक्सर यह जवाब मशीन के नज़रिए से मिलता है. यह ज़रूरी नहीं है कि यह जवाब, डेवलपर के नज़रिए से या ऐप्लिकेशन के रनटाइम के उनके मॉडल से मेल खाता हो. ऐसा तब ज़्यादा सच होता है, जब कुछ कार्रवाइयों को बाद में असाइनोक्रोनस तरीके से होने के लिए शेड्यूल किया जाता है: ऐसे में, ऐसे ऑपरेशन की “मूल वजह” या शेड्यूलिंग के बारे में जानना अब भी दिलचस्प हो सकता है, लेकिन यह ऐसा कुछ है जो असाइनोक्रोनस स्टैक ट्रेस का हिस्सा नहीं होगा.

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

इस समस्या को हल करने के लिए, DevTools console ऑब्जेक्ट पर “Async Stack Tagging API” नाम का एक तरीका दिखाता है. इससे फ़्रेमवर्क डेवलपर, उन जगहों के बारे में बता सकते हैं जहां ऑपरेशन शेड्यूल किए जाते हैं और जहां ये ऑपरेशन लागू किए जाते हैं.

Async Stack Tagging API

असाइन्टेक स्टैक टैगिंग के बिना, फ़्रेमवर्क के ज़रिए असाइन्टेक तरीके से जटिल तरीके से चलाए गए कोड के लिए स्टैक ट्रेस, उस कोड से कोई कनेक्शन नहीं दिखाता जहां इसे शेड्यूल किया गया था.

किसी ऐसे कोड का स्टैक ट्रेस जिसे असाइन किए जाने के बाद, उसे शेड्यूल किए जाने की जानकारी के बिना ही चला दिया गया. यह सिर्फ़ `requestAnimationFrame` से शुरू होने वाला स्टैक ट्रेस दिखाता है. हालांकि, इसमें शेड्यूल किए जाने के समय की कोई जानकारी नहीं होती.

एक साथ काम नहीं करने वाले स्टैक टैगिंग की मदद से, यह कॉन्टेक्स्ट दिया जा सकता है. स्टैक ट्रेस इस तरह दिखता है:

किसी ऐसे कोड का स्टैक ट्रेस जिसे शेड्यूल किए जाने के बाद, उसे एक साथ नहीं चलाया गया. इसमें, शेड्यूल किए जाने के समय की जानकारी भी शामिल होती है. ध्यान दें कि पहले के मुकाबले, इसमें स्टैक ट्रेस में `businessLogic` और `schedule` शामिल हैं.

ऐसा करने के लिए, console.createTask() नाम के नए console तरीके का इस्तेमाल करें. यह तरीका, एसिंक्रोनस स्टैक टैगिंग एपीआई उपलब्ध कराता है. इसका हस्ताक्षर इस तरह का है:

interface Console {
  createTask(name: string): Task;
}

interface Task {
  run<T>(f: () => T): T;
}

console.createTask() को कॉल करने पर, एक Task इंस्टेंस मिलता है. इसका इस्तेमाल बाद में, एसिंक्रोनस कोड चलाने के लिए किया जा सकता है.

// Task Creation
const task = console.createTask(name);

// Task Execution
task.run(f);

एसिंक्रोनस ऑपरेशन को नेस्ट भी किया जा सकता है. साथ ही, स्टैक ट्रेस में “मूल वजहें” क्रम से दिखेंगी.

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

Angular में एसिंक्रोनस स्टैक टैगिंग एपीआई

Angular में, NgZone में बदलाव किए गए हैं. यह Angular का एक एक्सीक्यूशन कॉन्टेक्स्ट है, जो असाइन किए गए सभी टास्क के लिए काम करता है.

टास्क शेड्यूल करते समय, उपलब्ध होने पर console.createTask() का इस्तेमाल किया जाता है. इससे मिलने वाले Task इंस्टेंस को, आगे इस्तेमाल करने के लिए सेव किया जाता है. टास्क को ट्रिगर करने पर, NgZone उसे चलाने के लिए सेव किए गए Task इंस्टेंस का इस्तेमाल करेगा.

ये बदलाव, पुल रिक्वेस्ट #46693 और #46958 के ज़रिए, Angular के NgZone 0.11.8 में किए गए हैं.

फ़्रेंडली कॉल फ़्रेम

फ़्रेमवर्क, प्रोजेक्ट बनाते समय अक्सर सभी तरह की टेंप्लेट लैंग्वेज से कोड जनरेट करते हैं. जैसे, Angular या JSX टेंप्लेट, जो एचटीएमएल जैसे दिखने वाले कोड को सामान्य JavaScript में बदल देते हैं. यह कोड, आखिर में ब्राउज़र में चलता है. कभी-कभी, जनरेट किए गए इस तरह के फ़ंक्शन को ऐसे नाम दिए जाते हैं जो आसानी से समझ में नहीं आते. जैसे, छोटा किए जाने के बाद एक अक्षर वाले नाम या ऐसे नाम जो समझ में न आएं या जो आम तौर पर इस्तेमाल न किए जाते हों.

Angular में, स्टैक ट्रेस में AppComponent_Template_app_button_handleClick_1_listener जैसे नामों वाले कॉल फ़्रेम देखना आम बात है.

अपने-आप जनरेट हुए फ़ंक्शन के नाम के साथ स्टैक ट्रेस का स्क्रीनशॉट.

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

Angular में फ़्रेंडली कॉल फ़्रेम

Angular में कॉल फ़्रेम के नाम बदलने की प्रोसेस जारी है. हमें उम्मीद है कि ये सुधार समय के साथ धीरे-धीरे लागू होंगे.

लेखकों के लिखे गए एचटीएमएल टेंप्लेट को पार्स करते समय, Angular कंपाइलर TypeScript कोड जनरेट करता है. इसे आखिर में JavaScript कोड में ट्रांसपाइल किया जाता है, जिसे ब्राउज़र लोड और चलाता है.

कोड जनरेशन की इस प्रोसेस के तहत, सोर्स मैप भी बनाए जाते हैं. फ़िलहाल, हम सोर्स मैप के “names” फ़ील्ड में फ़ंक्शन के नाम शामिल करने के तरीकों को एक्सप्लोर कर रहे हैं. साथ ही, जनरेट किए गए कोड और ओरिजनल कोड के बीच की मैपिंग में उन नामों का रेफ़रंस दे रहे हैं.

उदाहरण के लिए, अगर किसी इवेंट लिसनर के लिए कोई फ़ंक्शन जनरेट किया जाता है और उसका नाम आसान नहीं है या छोटा करने के दौरान हटा दिया गया है, तो सोर्स मैप में अब “names” फ़ील्ड में इस फ़ंक्शन के लिए आसान नाम शामिल किया जा सकता है. साथ ही, फ़ंक्शन के स्कोप की शुरुआत के लिए मैपिंग में अब इस नाम का रेफ़रंस दिया जा सकता है. इसका मतलब है कि पैरामीटर सूची का बायां ब्रैकेट. इसके बाद, Chrome DevTools इन नामों का इस्तेमाल करके, स्टैक ट्रेस में कॉल फ़्रेम का नाम बदल देगा.

आगे की योजना

अपने काम की पुष्टि करने के लिए, टेस्ट पायलट के तौर पर Angular का इस्तेमाल करना एक शानदार अनुभव रहा. हमें फ़्रेमवर्क डेवलपर से सुझाव, राय या शिकायत मिलना पसंद है. साथ ही, इन एक्सटेंशन पॉइंट के बारे में हमें सुझाव/राय दें या शिकायत करें.

हम ऐसे और भी क्षेत्रों को एक्सप्लोर करना चाहते हैं. खास तौर पर, DevTools में प्रोफ़ाइलिंग के अनुभव को बेहतर बनाने का तरीका.