มากกว่าแค่เรื่อง Missing Exports: การสร้าง Early Garbage Collector
ผมพยายามใช้ปลั๊กอินมาตรฐานเพื่อแก้ไขลิงก์ที่หายไปในเอกสารของ Webpack แต่มันล้มเหลว
การรวมปลั๊กอินเข้ากับ codebase ขนาดใหญ่นั้นไม่ใช่เรื่องง่าย แต่มันกลายเป็นความท้าทายด้านสถาปัตยกรรม ผมต้องจัดการทั้งการจัดการ Abstract Syntax Tree (AST), การใช้หน่วยความจำ และ recursive loop
ปัญหาของปลั๊กอินมาตรฐาน
ผมได้ทำการทดลอง 3 ครั้งเพื่อหาทางออก
การทดลองที่ 1: ปลั๊กอินสามารถกู้คืน type ได้ 135 รายการ อย่างไรก็ตาม มันกลับนำไปไว้ใน internal module ซึ่งเครื่องมือของเราสร้างโฟลเดอร์แยกต่างหากสำหรับสิ่งเหล่านี้ ทำให้เราต้องมาจัดเรียงด้วยตัวเอง นอกจากนี้ โครงสร้างของเอกสารยังผิดพลาดอีกด้วย
การทดลองที่ 2: ผมเปิดการตั้งค่าเพื่อซ่อน external types ซึ่งได้ผลในบางส่วน โดยช่วยลด payload ลงเหลือเพียง 60 Webpack types แต่ TypeDoc กลับมองข้าม nested dependencies ทำให้มีหลาย type ถูกซ่อนไว้ทั้งที่ยังคงใช้หน่วยความจำอยู่
การทดลองที่ 3: ผมพยายามแมป type กลับไปยังโมดูลต้นฉบับ แต่นี่ทำให้เกิด recursive loop ปลั๊กอินพยายามดึง nested interfaces ออกมาเรื่อยๆ จนสร้าง type ขึ้นมาถึง 630 รายการ ทำให้เอกสารเต็มไปด้วยข้อมูลขยะ (noise) และทำลายประสบการณ์การใช้งานของผู้ใช้
ทางออก: Early Garbage Collection
ผมต้องการให้ type อยู่ในโมดูลที่ถูกต้องโดยไม่มีข้อมูลขยะส่วนเกิน ผมจึงเลิกพยายามแก้ไขที่ผลลัพธ์ (output) และหันมาแก้ไขที่กระบวนการ (process) แทน
ผมใช้ hook ที่ชื่อว่า EVENT_RESOLVE_END ซึ่งช่วยให้ผมสามารถดักจับ (intercept) AST ได้ทันทีหลังจากการทำ resolution ผมทำขั้นตอนนี้ก่อนที่ TypeDoc จะกำหนดหมวดหมู่หรือเริ่มใช้หน่วยความจำจำนวนมาก
ตรรกะของผมประกอบด้วย 3 ขั้นตอน:
- ค้นหา internal module ใน AST
- ตรวจสอบทุก node โดยใช้ utility ที่เขียนขึ้นเอง
- ลบข้อมูลขยะ ผมใช้
project.removeReflectionเพื่อลบ node ที่ไม่จำเป็นออก 300 รายการทันที ซึ่งช่วยให้ Node.js คืนหน่วยความจำได้ - ย้าย type ที่สำคัญ ผมรวม 300 type ที่เหลือเข้ากับ root scope
ผลลัพธ์ที่ได้: ผมสามารถรักษา 240 vital types เอาไว้ได้ ซึ่งตอนนี้พวกมันแสดงผลได้อย่างถูกต้องและถูกจัดเส้นทาง (routed) อย่างเหมาะสม ผมสามารถหลีกเลี่ยงปัญหา noise จากการเรียกซ้ำแบบในการทดลองที่สามได้
บทเรียนที่ได้รับ
• จัดการ AST ตั้งแต่เนิ่นๆ การลบ node ที่ไม่จำเป็นออกจะช่วยป้องกันการสิ้นเปลืองหน่วยความจำในภายหลัง • ใช้ hooks แทนการใช้ hacks การเข้าใจ compiler lifecycle จะช่วยให้ทำงานได้อย่างสะอาดและเป็นระบบ • Feedback ช่วยพัฒนาสถาปัตยกรรม การรีวิวจาก Maintainer ช่วยให้ผมสร้างระบบที่ดีขึ้นได้ • ข้อมูลที่มากขึ้นไม่ได้แปลว่าดีกว่าเสมอไป วิศวกรรมที่ดีคือการหาจุดสมดุลระหว่างข้อมูลและความสามารถในการใช้งาน (usability)
เหนือกว่าการขาดหายไปของ Exports: การสร้าง Early Garbage Collector สำหรับ TypeDoc AST ของ Webpack
เมื่อทำงานกับโปรเจกต์ TypeScript ขนาดใหญ่ TypeDoc มักจะเป็นเครื่องมือหลักที่ถูกเลือกใช้สำหรับการสร้างเอกสาร อย่างไรก็ตาม เมื่อนำมาผสานรวมเข้ากับ Webpack build pipeline มันอาจกลายเป็นคอขวด (bottleneck) ที่สำคัญ ทั้งในด้านหน่วยความจำ (memory) และเวลาที่ใช้ในการประมวลผล
ปัญหา: AST ที่บวมเกินความจำเป็น
ปัญหาหลักอยู่ที่ Abstract Syntax Tree (AST) ที่สร้างโดย TypeDoc สำหรับ codebase ขนาดมหึมา AST นี้อาจมีขนาดใหญ่ถึงหลายร้อยเมกะไบต์ ซึ่งส่งผลกระทบโดยตรงต่อประสิทธิภาพของ Webpack และการใช้ทรัพยากรของระบบ
ปัญหาที่พบบ่อยคือ "Missing Exports" ซึ่งเป็นเพียงอาการหนึ่งที่เกิดขึ้นเมื่อ TypeDoc พยายามจะสร้างเอกสารสำหรับโมดูลหรือฟังก์ชันที่ไม่ได้ถูก export ออกมาอย่างถูกต้อง หรือเมื่อโครงสร้างการเชื่อมโยง (linkage) ใน AST ไม่สมบูรณ์ ทำให้ Type