رایان دال (Ryan Dahl)، سازندهی Node.js اخیراً یک سال و نیم روی Deno کار کرده است. Deno یک اجراکنندهی جاوا اسکریپت است که قرار است تمام مشکلات ذاتی Node را حل کند.
اشتباه برداشت نشود! Node یک اجراکنندهی جاوا اسکریپت سمت سرور عالی است. Node بیشتر به دلیل داشتن اکوسیستم وسیع و استفاده از جاوا اسکریپت در نوع خودش بسیار خوب است. هرچند آقای دال اعتراف میکند که چند چیز هست که باید دربارهی آنها بیشتر فکر میکرده است. مثل امنیت، ماژولها و وابستگیها.
در دفاع از او باید گفت که او تصور نمیکرد که پلتفرمش در یک بازهی زمانی کوتاه، در این سطح رشد کند. همچنین، در سال 2009، جاوا اسکریپت هنوز یک زبان عجیب و غریب بود و همه آن را مسخره میکردند. حتی خیلی از ویژگیهای الآن این زبان، در آن زمان وجود نداشتند.
Deno چیست و ویژگیهای اصلیاش چه چیزهایی است؟
Deno یک اجراکنندهی امن اسکریپت روی V8 است. V8 موتور اجرای گوگل برای جاوا اسکریپت است.
Deno با استفاده از چه چیزهایی ساختهشده است:
- Rust – هستهی Deno با استفاده از Rust نوشتهشده است. (هستهی Node با ++C نوشتهشده است.)
- Tokio – حلقهی رخدادها که در Rust نوشتهشده است.
- TypeScript – Deno بهتنهایی و بدون نیاز به چیز دیگری از JavaScript و TypeScript پشتیبانی میکند.
- V8 – موتور اجرای گوگل برای جاوا اسکریپت که در Chrome و Node استفاده میشود.
در ادامه میخواهیم امکاناتی را که Deno ارائه میکند را باهم بررسی کنیم.
امنیت (دسترسیها)
از مهمترین ویژگیهای Deno، تمرکز روی بحث امنیت است.
برخلاف Node، در Deno بهصورت پیشفرض کدها در سَندباکس (Sandbox) اجرا میشوند. Sandbox، اصطلاحی است که به معنی یک محیط جداگانه برای تست کدها است. این یعنی کد اجرایی بههیچعنوان به موارد زیر دسترسی ندارد:
- فایل سیستم
- شبکه
- روند اجرای اسکریپتهای دیگر
- سایر متغیرهای محیط برنامه
در ادامه میخواهیم نحوهی کار دسترسیهای سیستم را بررسی کنیم.
(async () => { const encoder = new TextEncoder(); const data = encoder.encode('Hello world\n'); await Deno.writeFile('hello.txt', data); await Deno.writeFile('hello2.txt', data); })();
اسکریپت دادهشده دو فایل بانامهای hello.txt
و hello2.txt
ایجاد میکند که یک پیام Hello world
درون آنها قرار دارد. این کد در Sandbox اجرا خواهد شد. پس به فایل سیستم دسترسی نخواهد داشت.
همچنین در نظر داشته باشید که ما بهجای استفاده از ماژولِ FS، در حال استفاده از فضای نامِ (Namespace) Deno هستیم. فضای نامِ Deno توابعِ کمکیِ اساسیِ زیادی را در اختیار ما قرار میدهد. ما با استفاده کردن از فضایِ نام، سازگاری مرورگر را از دست میدهیم. در این مورد در ادامه صحبت خواهیم کرد.
وقتی ما آن را با استفاده از فرمان زیر اجرا کنیم:
deno run write-hello.ts
موارد زیر از ما خواسته خواهد شد:
Deno requests write access to "/Users/user/folder/hello.txt". Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)]
درواقع، این مورد دو بار از ما خواسته خواهد شد. زیرا هر فراخوانی از Sandbox، نیاز به دریافت مجوز دارد. اما اگر ما allow always
را انتخاب کنیم؛ فقط همین یکبار از ما مجوز خواهد خواست.
اگر ما گزینهی Deny
را انتخاب کنیم؛ خطای PermissionDenied
رخ میدهد و پردازش خاتمه مییابد. زیرا در این حالت ما هیچ منطقِ رسیدگی به خطایی نداریم.
وقتی ما اسکریپت موردنظر را با فرمان زیر اجرا کنیم:
deno run --allow-write write-hello.ts
درخواستی برای دادن دسترسی داده نمیشود و هر دو فایل ساخته خواهند شد.
Flag ای که در کد بالا مشاهده کردید ( --allow-write
) مربوط به دادن دسترسی مربوط به فایل سیستم بود. Flagهای –allow-net
و –allow-env
و –allow-run
به ترتیب مربوط به تقاضاهای دسترسی به شبکه، محیط و اجرای زیر پردازشها است.
ماژولها
Deno همانند مرورگرها، ماژولها را از طریق URL لود و بارگذاری میکند. افراد زیادی در ابتدا وقتی یک Statement را در سمت سرور میبینند؛ تعجب میکنند. اما درواقع این یکچیز منطقی است. به مورد زیر توجه کنید:
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
در اینجا شما ممکن است این سؤال را بپرسید. مسئلهی عجیبی که با ایمپورت کردن پکیجها از طریق URL آنها به وجود میآید چیست؟ جواب این سؤال ساده است. پکیجهای Deno میتوانند بدون یک رجیستری متمرکز مانند npm و بهصورت توزیعشده باشند. این مسئله اخیراً مشکلات زیادی را به وجود آورده است.
با ایمپورت کردن کد از طریق URL، ما امکانی را فراهم میکنیم که سازندگان پکیجها، در هر هاستی که خودشان مناسب میدانند؛ از کدشان میزبانی کنند. یعنی به بهترین شکل غیرمتمرکز سازی را انجام میدهیم. دیگر نیازی به package.json و node_modules نخواهد بود.
وقتی ما برنامه را اجرا میکنیم؛ Deno همهی ماژولهای ایمپورت شده را دانلود کرده و آنها را کَش (Cache) میکند. وقتی آنها کَش شدند؛ Deno دیگر آنها را مجدداً دانلود نخواهد کرد. تا زمانی که ما بهطور خاص با استفاده از –reload از Deno بخواهیم که این کار را انجام دهد.
چند سؤال مهم در اینجا مطرح میشود:
اگر یک وبسایت خراب (Down) شود چه میشود؟
وقتیکه در Deno ما یک رجیستری متمرکز نداریم؛ وبسایتهایی که ماژولهای ما روی آنها قرار دارند ممکن است به دلایل مختلفی Down شوند. دلایلی مانند ایجاد توسعه یا تغییرات در آن. پس این مسئله ریسک زیادی به همراه دارد.
همانطور که قبلاً هم اشاره کردیم؛ Deno ماژولهای دانلود شده را کَش میکند. همچنین ازآنجاییکه کَش در حافظهی دیسکِ محلی ما ذخیره میشود؛ سازندگان Deno پیشنهاد کردهاند که آن را در سیستمهای کنترل ورژن مانند git بررسی کنیم و دائماً آن را در مخزن حافظهی سیستم نگهداریم. با این روش، حتی وقتی وبسایت Down میشود؛ همهی توسعهدهندهها میتوانند دسترسی خودشان را از طریق نسخهی دانلود شده حفظ کنند.
Deno کَش را در دایرکتوریِ اختصاص دادهشده در داخل متغیر محیطیِ $DENO_DIR ذخیره میکند. اگر خودمان متغیر را تنظیم نکنیم؛ در کَش دایرکتوری پیشفرض سیستم تنظیم میشود. ما میتوانیم $DENO_DIR را درجایی در مخزن محلی خودمان تنظیم کنیم و از طریق سیستم کنترل ورژن آن را بررسی کنیم.
آیا همیشه باید آن را از طریق URL ایمپورت کنیم؟
اینکه دائماً بخواهیم URL تایپ کنیم؛ کار خستهکنندهای است. خوشبختانه، Deno برای اجتناب از این کار دو انتخاب برای ما قرار داده است.
اولین انتخاب این است که ماژولِ ایمپورت شده از فایل محلی را مجدداً اکسپورت کنیم. به این صورت که:
export { test, assertEquals } from "https://deno.land/std/testing/mod.ts";
فرض میکنیم فایل بالا local-test-utils.ts نام دارد. حالا، اگر ما بخواهیم دوباره از یکی از توابع test یا assertEquals استفاده کنیم؛ میتوانیم بهصورت زیر به آن ارجاع دهیم:
import { test, assertEquals } from './local-test-utils.ts';
پس زیاد مهم نیست که حتماً از طریق URL لود شود یا نشود.
انتخاب دوم، ساختنِ یک نقشهی ایمپورتها است. این نقشه در یک فایل JSON مشخص میشود:
{ "imports": { "http/": "https://deno.land/std/http/" }}
و سپس آن را بهصورت زیر ایمپورت میکنیم:
import { serve } from "http/server.ts";
برای اینکه این کد کار کند؛ ما باید به Deno اعلام کنیم که یک نقشهی ایمپورت داریم. این کار را با نوشتن Flag ِ: –importmap انجام میدهیم.
deno run --importmap=import_map.json hello_server.ts
نسخه سازی پکیجها به چه صورت است؟
نسخه سازی یا ورژن سازی باید توسط ارائهکنندهی پکیج پشتیبانی شود. اما از طرف کلاینت، این قابلیت کاهش مییابد. در سمت کلاینت فقط میتوان شمارهی ورژن را در URL تنظیم کرد. به این صورت:
https://unpkg.com/liltest@0.0.5/dist/liltest.js
سازگاری با مرورگرها
هدف Deno این است که با مرورگرها سازگار باشد. بیایید کمی فنیتر صحبت کنیم. وقتی از ماژولِ ES استفاده کنیم؛ نیازی نیست که از هیچ ابزار ساختی مانند webpack برای آماده کردن برنامهی خودمان برای اجرا در مرورگر استفاده کنیم.
بااینحال، ابزارهایی مانند Babel، کدها را به ورژنِ ES5 از جاوا اسکریپت قابلانتقال میکنند. درنتیجه، کد موردنظر حتی در مرورگرهای قدیمیتر که از امکانات جدید این زبان پشتیبانی نمیکنند هم قابلاجرا خواهد بود. اما این ویژگی هزینهای هم دارد. باید مقدار زیادی از کدهای غیرضروری را در فایل نهایی قرار دهیم که باعث پرحجم شدن فایل خروجی نهایی خواهد شد.
بنابراین تصمیمگیری به عهدهی خود ما است. اینکه چه هدفی داریم و بر اساس آن هدف روش موردنظر را انتخاب کنیم.
پشتیبانی از TypeScript بهصورت مستقل
Deno بهراحتی استفاده از TypeScript را بدون احتیاج به فایل پیکربندی ممکن میکند. همچنین نوشتن برنامهها در جاوا اسکریپت ساده و اجرای آنها با Deno بدون هیچ مشکلی امکانپذیر است.
خلاصه
Deno، یک اجراکنندهی جدید برای تایپ اسکریپت و جاوا اسکریپت است. Deno یک پروژهی جذاب است که در حال حاضر بهطور پیوسته در حال رشد است. اما بااینحال، راه درازی دارد تا بتوانیم آن را یک محصول کامل فرض کنیم.
رویکرد Deno غیرمتمرکزسازی است. همین رویکرد یک گام ضروری برای آزاد کردن جاوا اسکریپت از پکیج متمرکز رجیستری یا همان npm است.
شما میتوانید به نسخهی انتشار یافتهی Deno از طریق گیت هاب دسترسی پیدا کنید.