در این مقاله ، با ایجاد یک ابزار خط فرمان که می تواند متن را در فایل ها و پوشه ها جستجو کند ، در مورد معرفی خود با Deno تلاش خواهیم کرد. ما از طیف وسیعی از روشهای API استفاده خواهیم کرد که Deno برای خواندن و نوشتن در سیستم فایل ارائه می دهد.

در آخرین اقساط ، ما از Deno برای ساختن یک ابزار خط فرمان برای درخواست از API شخص ثالث استفاده کردیم. در این مقاله ، ما می خواهیم شبکه را به یک طرف بسپاریم و ابزاری را بسازیم که به شما امکان می دهد متن فایل ها و پوشه ها را در فهرست پرونده فعلی خود در سیستم فایل جستجو کنید – مانند ابزارهایی مانند grep.

توجه: ما در حال ساخت ابزاری نیستیم که به اندازه بهینه و کارآمد باشد grepو نه ما قصد داریم آن را جایگزین کنیم! هدف از ساخت ابزاری مانند این آشنایی با API های سیستم فایل Deno است.

در حال نصب Deno

ما تصور خواهیم کرد که شما Deno را بطور محلی بر روی دستگاه خود اجرا کرده و در حال کار هستید. می توانید بررسی کنید این وب سایت یا برای کسب اطلاعات بیشتر در مورد چگونگی افزودن پشتیبانی Deno به ویرایشگر مورد نظر خود یا مقاله قبلی.

در زمان نوشتن ، آخرین نسخه پایدار Deno است 1.10.2، بنابراین این چیزی است که من در این مقاله استفاده می کنم.

تنظیم دستور جدید ما با Yargs

مانند مقاله قبلی ، ما استفاده خواهیم کرد یارگ ها برای ایجاد رابطی که کاربران ما می توانند برای اجرای ابزار ما استفاده کنند. بیایید ایجاد کنیم index.ts و آن را با موارد زیر پر کنید:

import yargs from "https://deno.land/x/yargs@v17.0.1-deno/deno.ts";

interface Yargs<ArgvReturnType> {
  describe: (param: string, description: string) => Yargs<ArgvReturnType>;
  demandOption: (required: string[]) => Yargs<ArgvReturnType>;
  argv: ArgvReturnType;
}

interface UserArguments {
  text: string;
}

const userArguments: UserArguments =
  (yargs(Deno.args) as unknown as Yargs<UserArguments>)
    .describe("text", "the text to search for within the current directory")
    .demandOption(["text"])
    .argv;

console.log(userArguments);

یک چیز منصفانه در اینجا جریان دارد که قابل ذکر است:

  • Yargs را با اشاره به مسیر آن در مخزن Deno نصب می کنیم. من به طور واضح از یک شماره نسخه دقیق استفاده می کنم تا اطمینان حاصل کنم که همیشه آن نسخه را دریافت می کنیم ، بنابراین در پایان وقتی اسکریپت اجرا می شود از آخرین نسخه استفاده نمی کنیم.
  • در زمان نوشتن مقاله ، تجربه Deno + TypeScript برای Yargs عالی نیست ، بنابراین من رابط کاربری خودم را ایجاد کردم و از آن برای ایجاد برخی از انواع ایمنی استفاده کردم.
  • UserArguments شامل تمام ورودی هایی است که ما از کاربر درخواست خواهیم کرد. در حال حاضر ، ما فقط می خواهیم درخواست کنیم text، اما در آینده ما می توانیم این را گسترش دهیم تا به جای فرض فهرست فعلی ، لیستی از فایلها را برای جستجو ارائه دهیم.

ما می توانیم این کار را با deno run index.ts و خروجی Yargs ما را ببینید:

$ deno run index.ts
Check file:///home/jack/git/deno-file-search/index.ts
Options:
  --help     Show help                                                 [boolean]
  --version  Show version number                                       [boolean]
  --text     the text to search for within the current directory      [required]

Missing required argument: text

اکنون زمان اجرای آن فرا رسیده است!

فهرست کردن پرونده ها

قبل از شروع جستجوی متن در یک فایل داده شده ، باید لیستی از فهرست ها و پرونده ها برای جستجو در داخل ایجاد کنیم. Deno فراهم می کند Deno.readdir، که بخشی از کتابخانه “داخلی” است ، به این معنی که شما مجبور نیستید آن را وارد کنید. این در فضای نام جهانی برای شما در دسترس است.

Deno.readdir ناهمزمان است و لیستی از پرونده ها و پوشه ها را در فهرست فعلی برمی گرداند. این موارد را به صورت AsyncIterator، این بدان معنی است که ما باید از for await ... of حلقه برای رسیدن به نتایج:

for await (const fileOrFolder of Deno.readDir(Deno.cwd())) {
  console.log(fileOrFolder);
}

این کد از فهرست کار فعلی خوانده می شود (که Deno.cwd() به ما می دهد) و هر نتیجه را وارد کنید. با این حال ، اگر اکنون سعی کنید اسکریپت را اجرا کنید ، با خطا مواجه خواهید شد:

$ deno run index.ts --text='foo'
error: Uncaught PermissionDenied: Requires read access to <CWD>, run again with the --allow-read flag
for await (const fileOrFolder of Deno.readDir(Deno.cwd())) {
                                                   ^
    at deno:core/core.js:86:46
    at unwrapOpResult (deno:core/core.js:106:13)
    at Object.opSync (deno:core/core.js:120:12)
    at Object.cwd (deno:runtime/js/30_fs.js:57:17)
    at file:///home/jack/git/deno-file-search/index.ts:19:52

به یاد داشته باشید که Deno برای خواندن از سیستم فایل به صریح اسکریپت ها نیاز دارد. در مورد ما ، --allow-read پرچم کد ما را برای اجرای فعال می کند:

~/$ deno run --allow-read index.ts --text='foo'
{ name: ".git", isFile: false, isDirectory: true, isSymlink: false }
{ name: ".vscode", isFile: false, isDirectory: true, isSymlink: false }
{ name: "index.ts", isFile: true, isDirectory: false, isSymlink: false }

در این حالت ، من اسکریپت را در دایرکتوری که در حال ساخت ابزار خود هستم ، اجرا می کنم ، بنابراین کد منبع TS را پیدا می کند .git مخزن و .vscode پوشه بیایید شروع به نوشتن برخی توابع کنیم تا به صورت بازگشتی به این ساختار برویم ، زیرا ما باید تمام فایلهای موجود در فهرست را پیدا کنیم ، نه فقط موارد سطح بالا. علاوه بر این ، ما می توانیم برخی از نادیده گرفتن های رایج را اضافه کنیم. فکر نمی کنم کسی بخواهد اسکریپت کل آن را جستجو کند .git پوشه!

در کد زیر ، ما ایجاد کرده ایم getFilesList عملکرد ، که یک دایرکتوری را می گیرد و تمام پرونده های موجود در آن دایرکتوری را برمی گرداند. اگر با یک دایرکتوری روبرو شود ، به صورت بازگشتی تماس می گیرد تا فایل های تو در تو را پیدا کند و نتیجه را بازگرداند:

const IGNORED_DIRECTORIES = new Set([".git"]);

async function getFilesList(
  directory: string,
): Promise<string[]> {
  const foundFiles: string[] = [];
  for await (const fileOrFolder of Deno.readDir(directory)) {
    if (fileOrFolder.isDirectory) {
      if (IGNORED_DIRECTORIES.has(fileOrFolder.name)) {
        
        continue;
      }
      
      const nestedFiles = await getFilesList(
        `${directory}/${fileOrFolder.name}`,
      );
      foundFiles.push(...nestedFiles);
    } else {
      
      foundFiles.push(`${directory}/${fileOrFolder.name}`);
    }
  }
  return foundFiles;
}

سپس می توانیم از این مانند استفاده کنیم:

const files = await getFilesList(Deno.cwd());
console.log(files);

ما همچنین برخی از خروجی ها را دریافت می کنیم که خوب به نظر می رسد:

$ deno run --allow-read index.ts --text='foo'
[
  "/home/jack/git/deno-file-search/.vscode/settings.json",
  "/home/jack/git/deno-file-search/index.ts"
]

با استفاده از path مدول

اکنون می توانیم مسیرهای پرونده را با رشته های الگو ترکیب کنیم:

`${directory}/${fileOrFolder.name}`,

اما انجام این کار با استفاده از Deno بهتر است path مدول. این ماژول یکی از ماژول هایی است که Deno به عنوان بخشی از کتابخانه استاندارد خود ارائه می دهد (دقیقاً مانند Node که با آن کار می کند path ماژول) ، و اگر از Node استفاده کرده باشید path کد ماژول بسیار شبیه به نظر می رسد. در زمان نوشتن ، آخرین نسخه از std کتابخانه Deno فراهم می کند 0.97.0، و ما واردات path ماژول از mod.ts فایل:

import * as path from "https://deno.land/std@0.97.0/path/mod.ts";

mod.ts هنگام وارد کردن ماژول های استاندارد Deno همیشه نقطه ورود است. مستندات این ماژول در سایت Deno زندگی می کند و لیست ها path.join، که چندین مسیر را طی می کند و آنها را در یک مسیر قرار می دهد. بیایید این عملکرد را به جای ترکیب دستی آنها وارد کنیم و از آنها استفاده کنیم:


import yargs from "https://deno.land/x/yargs@v17.0.1-deno/deno.ts";
import * as path from "https://deno.land/std@0.97.0/path/mod.ts";


async function getFilesList(
  directory: string,
): Promise<string[]> {
  const foundFiles: string[] = [];
  for await (const fileOrFolder of Deno.readDir(directory)) {
    if (fileOrFolder.isDirectory) {
      if (IGNORED_DIRECTORIES.has(fileOrFolder.name)) {
        
        continue;
      }
      
      const nestedFiles = await getFilesList(
        path.join(directory, fileOrFolder.name),
      );
      foundFiles.push(...nestedFiles);
    } else {
      
      foundFiles.push(path.join(directory, fileOrFolder.name));
    }
  }
  return foundFiles;
}

هنگام استفاده از کتابخانه استاندارد ، یادآوری این نکته ضروری است که به نسخه خاصی پین بزنید. بدون این کار ، کد شما همیشه آخرین نسخه را بارگیری می کند ، حتی اگر این شامل تغییراتی باشد که کد شما را خراب می کند. اسناد Deno در مورد کتابخانه استاندارد بیشتر به این موضوع بپردازید ، و من توصیه می کنم به آن صفحه مطالعه کنید.

خواندن محتوای یک فایل

بر خلاف Node ، که به شما امکان می دهد محتویات پرونده ها را از طریق fs ماژول و readFile روش ، Deno ارائه می دهد readTextFile خارج از جعبه به عنوان بخشی از هسته اصلی آن ، به این معنی که در این حالت ما نیازی به وارد کردن ماژول های اضافی نداریم. readTextFile فرض می کند که فایل به صورت UTF-8 رمزگذاری شده است – که برای پرونده های متنی ، معمولاً همان چیزی است که شما می خواهید. اگر با رمزگذاری پرونده دیگری کار می کنید ، می توانید از نسخه عمومی تر استفاده کنید readFile، که در مورد رمزگذاری چیزی فرض نمی کند و به شما امکان می دهد از یک رمزگشای خاص عبور کنید.

هنگامی که لیست پرونده ها را دریافت کردیم ، می توانیم روی آنها حلقه بزنیم و محتوای آنها را به عنوان متن بخوانیم:

const files = await getFilesList(Deno.cwd());

files.forEach(async (file) => {
  const contents = await Deno.readTextFile(file);
  console.log(contents);
});

از آنجا که وقتی می خواهیم مطابقت پیدا کنیم شماره خط را بدانیم ، می توانیم محتویات را روی یک خط جدید تقسیم کنیم (n) و هر خط را به نوبه خود جستجو کنید تا ببینید آیا مطابقت دارد یا خیر. به این ترتیب ، در صورت وجود ، ما شاخص شماره خط را می دانیم تا بتوانیم آن را به کاربر گزارش دهیم:

files.forEach(async (file) => {
  const contents = await Deno.readTextFile(file);
  const lines = contents.split("n");
  lines.forEach((line, index) => {
    if (line.includes(userArguments.text)) {
      console.log("MATCH", line);
    }
  });
});

برای ذخیره مسابقات خود ، می توانیم رابطی ایجاد کنیم که نشان دهنده a باشد Match، و هنگامی که کبریت ها را پیدا کردیم به یک آرایه فشار می دهیم:

interface Match {
  file: string;
  line: number;
}
const matches: Match[] = [];
files.forEach(async (file) => {
  const contents = await Deno.readTextFile(file);
  const lines = contents.split("n");
  lines.forEach((line, index) => {
    if (line.includes(userArguments.text)) {
      matches.push({
        file,
        line: index + 1,
      });
    }
  });
});

سپس می توانیم مسابقات را از سیستم خارج کنیم:

matches.forEach((match) => {
  console.log(match.file, "line:", match.line);
});

با این حال ، اگر اکنون اسکریپت را اجرا کرده و متن متناسب با آن را ارائه دهید ، هنوز مطابقت با کنسول مشاهده نخواهید شد. این یک اشتباه رایج است که مردم مرتکب می شوند async و await در یک forEach زنگ زدن؛ forEach قبل از اینکه خود را انجام دهید صبر کنید تا تماس مجدد کامل شود. این کد را بگیرید:

files.forEach(file => {
  new Promise(resolve => {
    ...
  })
})

موتور جاوا اسکریپت در حال اجرا است forEach که بر روی هر فایل اجرا می شود – ایجاد یک وعده جدید – و سپس اجرای مابقی کد را ادامه می دهد. قرار نیست به طور خودکار منتظر حل آن وعده ها بماند و در زمان استفاده دقیقاً یکسان است await.

خبر خوب این است که این همانطور که انتظار می رود کار خواهد کرد for ... of حلقه ، بنابراین به جای:

files.forEach(file => {...})

ما می توانیم به:

for (const file of files) {
  ...
}

for ... of حلقه کد هر فایل را به صورت سری اجرا می کند و با مشاهده استفاده از await کلید واژه تا زمانی که وعده حل نشود ، اجرای آن را موقتاً متوقف خواهد کرد. این بدان معنی است که بعد از اجرای حلقه ، ما می دانیم که همه وعده ها برطرف شده اند ، و اکنون مسابقات را وارد صفحه می کنیم:

$ deno run --allow-read index.ts --text='readTextFile'
Check file:///home/jack/git/deno-file-search/index.ts
/home/jack/git/deno-file-search/index.ts line: 54

بیایید برخی از پیشرفت ها را برای تسهیل در خواندن انجام دهیم. به جای ذخیره کبریت ها به عنوان یک آرایه ، بیایید آن را a کنیم Map که در آن کلیدها نام پرونده هستند و مقدار آن یک است Set از همه مسابقات به این ترتیب ، ما می توانیم با لیست بندی موارد منطبق بر اساس فایل ، خروجی خود را روشن کنیم و یک ساختار داده داریم که به ما امکان می دهد داده ها را راحت تر کشف کنیم.

ابتدا می توانیم ساختار داده را ایجاد کنیم:

const matches = new Map<string, Set<Match>>();

سپس می توانیم کبریت ها را با افزودن آنها به a ذخیره کنیم Set برای آن پرونده داده شده این کار کمی بیشتر از قبل است. اکنون نمی توانیم فقط موارد را به داخل آرایه فشار دهیم. در وهله اول ما باید هرگونه تطابق موجود را پیدا کنیم (یا یک مورد جدید ایجاد کنیم) Set) و سپس آنها را ذخیره کنید:

for (const file of files) {
  const contents = await Deno.readTextFile(file);
  const lines = contents.split("n");
  lines.forEach((line, index) => {
    if (line.includes(userArguments.text)) {
      const matchesForFile = matches.get(file) || new Set<Match>();
      matchesForFile.add({
        file,
        line: index + 1,
      });
      matches.set(file, matchesForFile);
    }
  });
}

سپس ما می توانیم مسابقات را با تکرار روی ثبت کنیم Map. وقتی استفاده می کنید for ... of در یک Map، هر تکرار به شما آرایه ای از دو مورد را می دهد ، جایی که اولین کلید در نقشه است و دوم مقدار:

for (const match of matches) {
  const fileName = match[0];
  const fileMatches = match[1];
  console.log(fileName);
  fileMatches.forEach((m) => {
    console.log("=>", m.line);
  });
}

ما می توانیم تخریب هایی انجام دهیم تا این موضوع کمی ظریف تر شود:

for (const match of matches) {
  const [fileName, fileMatches] = match;

یا حتی:

for (const [fileName, fileMatches] of matches) {

اکنون وقتی اسکریپت را اجرا می کنیم می توانیم همه موارد منطبق را در یک فایل داده شده مشاهده کنیم:

$ deno run --allow-read index.ts --text='Deno'
/home/jack/git/deno-file-search/index.ts
=> 15
=> 26
=> 45
=> 54

در آخر ، برای اینکه خروجی کمی واضح تر باشد ، بیایید خط واقعی را که مطابقت دارد نیز ذخیره کنیم. ابتدا ، من به روز رسانی می کنم Match رابط:

interface Match {
  file: string;
  lineNumber: number;
  lineText: string;
}

سپس کدی که مسابقات را ذخیره می کند به روز کنید. یک نکته بسیار خوب در مورد TypeScript در اینجا این است که می توانید نسخه را به روز کنید Match سپس از کامپایلر بخواهید کدی را که برای به روزرسانی نیاز دارید به شما بگوید. من اغلب یک نوع را به روز می کنم و سپس منتظر می مانم VS Code هر مشکلی را برجسته کند. اگر همه مکان هایی را که کد برای به روزرسانی نیاز دارد کاملاً بخاطر بسپارید ، این یک روش بسیار مفید برای کار است:

if (line.includes(userArguments.text)) {
  const matchesForFile = matches.get(file) || new Set<Match>();
  matchesForFile.add({
    file,
    lineNumber: index + 1,
    lineText: line,
  });
  matches.set(file, matchesForFile);
}

کدی که از موارد منطبق خارج می شود نیز نیاز به به روزرسانی دارد

for (const [fileName, fileMatches] of matches) {
  console.log(fileName);
  fileMatches.forEach((m) => {
    console.log("=>", m.lineNumber, m.lineText.trim());
  });
}

تصمیم گرفتم تماس بگیرم trim() بر روی … ما lineText بنابراین ، اگر خط همسان شده به شدت تورفتگی داشته باشد ، ما آن را در نتایج نشان نمی دهیم. ما هر فضای خالی پیشرو (و دنباله دار) را در خروجی خود حذف خواهیم کرد.

و با این اوصاف ، می گویم نسخه اول ما تمام شد!

$ deno run --allow-read index.ts --text='Deno'
Check file:///home/jack/git/deno-file-search/index.ts
/home/jack/git/deno-file-search/index.ts
=> 15 (yargs(Deno.args) as unknown as Yargs<UserArguments>)
=> 26 for await (const fileOrFolder of Deno.readDir(directory)) {
=> 45 const files = await getFilesList(Deno.cwd());
=> 55 const contents = await Deno.readTextFile(file);

فیلتر کردن با پسوند پرونده

بیایید این قابلیت را گسترش دهیم تا کاربران بتوانند پسوندهای پرونده را که مطابقت داریم از طریق an فیلتر کنیم extension flag ، که کاربر می تواند پسوندی را به آن منتقل کند (مانند --extension js فقط مطابقت داشته باشد .js فایل ها). ابتدا کد Yargs و انواع آن را به روز کنیم تا به کامپایلر بگوییم که ما یک پرچم پسوند (اختیاری) را می پذیریم:

interface UserArguments {
  text: string;
  extension?: string;
}

const userArguments: UserArguments =
  (yargs(Deno.args) as unknown as Yargs<UserArguments>)
    .describe("text", "the text to search for within the current directory")
    .describe("extension", "a file extension to match against")
    .demandOption(["text"])
    .argv;

سپس می توانیم به روز کنیم getFilesList بنابراین یک آرگومان دوم اختیاری را می گیرد ، که می تواند یک شی از ویژگی های پیکربندی باشد که می توانیم آن را به تابع منتقل کنیم. من اغلب دوست دارم توابع یک شی از موارد پیکربندی را بگیرند ، زیرا اضافه کردن موارد بیشتر به آن شی بسیار آسان تر از به روزرسانی عملکرد برای نیاز به انتقال پارامترهای بیشتری است:

interface FilterOptions {
  extension?: string;
}

async function getFilesList(
  directory: string,
  options: FilterOptions = {},
): Promise<string[]> {}

اکنون در متن تابع ، هنگامی که یک پرونده را پیدا کردیم ، اکنون این مورد را بررسی می کنیم:

  • کاربر ارائه نمی دهد extension برای فیلتر کردن.
  • کاربر ارائه داد extension برای فیلتر کردن ، و پسوند پرونده با آنچه آنها ارائه می دهند مطابقت دارد. ما میتوانیم استفاده کنیم path.extname، که پسوند پرونده را برای یک مسیر داده شده برمی گرداند (برای foo.ts، باز می گردد .ts، بنابراین پسوندی را که کاربر در آن وارد شده است می گیریم و از آن استفاده می کنیم . به آن)
async function getFilesList(
  directory: string,
  options: FilterOptions = {},
): Promise<string[]> {
  const foundFiles: string[] = [];
  for await (const fileOrFolder of Deno.readDir(directory)) {
    if (fileOrFolder.isDirectory) {
      if (IGNORED_DIRECTORIES.has(fileOrFolder.name)) {
        
        continue;
      }
      
      const nestedFiles = await getFilesList(
        path.join(directory, fileOrFolder.name),
        options,
      );
      foundFiles.push(...nestedFiles);
    } else {
      

      
      const shouldStoreFile = !options.extension ||
        path.extname(fileOrFolder.name) === `.${options.extension}`;

      if (shouldStoreFile) {
        foundFiles.push(path.join(directory, fileOrFolder.name));
      }
    }
  }
  return foundFiles;
}

سرانجام ، ما باید تماس خود را به getFilesList عملکرد ، برای عبور از پارامترهایی که کاربر وارد کرده است:

const files = await getFilesList(Deno.cwd(), userArguments);

پیدا و جایگزین کنید

برای پایان کار ، بیایید ابزار خود را گسترش دهیم تا جایگزینی اساسی را فراهم کند. اگر کاربر عبور کند --replace=foo، ما هر تطبیقی ​​را که از جستجوی آنها پیدا کردیم ، می گیریم و کلمه ارائه شده را جایگزین آنها می کنیم – در این مورد ، foo، قبل از نوشتن آن پرونده بر روی دیسک. ما میتوانیم استفاده کنیم Deno.writeTextFile برای انجام این. (درست مثل با readTextFile، شما همچنین می توانید استفاده کنید writeFile اگر به کنترل بیشتری بر روی رمزگذاری نیاز دارید.)

یک بار دیگر ، ما ابتدا کد Yargs خود را به روز می کنیم تا اجازه ارائه استدلال را بدهیم:

interface UserArguments {
  text: string;
  extension?: string;
  replace?: string;
}

const userArguments: UserArguments =
  (yargs(Deno.args) as unknown as Yargs<UserArguments>)
    .describe("text", "the text to search for within the current directory")
    .describe("extension", "a file extension to match against")
    .describe("replace", "the text to replace any matches with")
    .demandOption(["text"])
    .argv;

کاری که اکنون می توانیم انجام دهیم این است که کد خود را که بر روی هر پرونده جداگانه قرار دارد به روز کنیم تا هرگونه تطابق را جستجو کنیم. هنگامی که هر خط را برای مطابقت بررسی کردیم ، می توانیم از آن استفاده کنیم replaceAll روش (این یک روش نسبتاً جدید است جاوا اسکریپت تعبیه شده است) برای گرفتن محتویات پرونده و جابجایی هر مسابقه با متن جایگزین ارائه شده توسط کاربر:

for (const file of files) {
  const contents = await Deno.readTextFile(file);
  const lines = contents.split("n");
  lines.forEach((line, index) => {
    if (line.includes(userArguments.text)) {
      const matchesForFile = matches.get(file) || new Set<Match>();
      matchesForFile.add({
        file,
        lineNumber: index + 1,
        lineText: line,
      });
      matches.set(file, matchesForFile);
    }
  });

  if (userArguments.replace) {
    const newContents = contents.replaceAll(
      userArguments.text,
      userArguments.replace,
    );
    
  }
}

نوشتن روی دیسک یک مورد تماس است writeTextFile، مسیر فایل و مطالب جدید را ارائه دهید:

if (userArguments.replace) {
  const newContents = contents.replaceAll(
    userArguments.text,
    userArguments.replace,
  );
  await Deno.writeTextFile(file, newContents);
}

در هنگام اجرای این ، اکنون خطای مجوزها دریافت خواهیم کرد. Deno خواندن پرونده و نوشتن پرونده را به مجوزهای جداگانه تقسیم می کند ، بنابراین شما باید از آنها عبور کنید --allow-write پرچم گذاری برای جلوگیری از خطا:

$ deno run --allow-read index.ts --text='readTextFile' --extension=ts --replace='jackWasHere'
Check file:///home/jack/git/deno-file-search/index.ts
error: Uncaught (in promise) PermissionDenied: Requires write access to "/home/jack/git/deno-file-search/index.ts", run again with the --allow-write flag
    await Deno.writeTextFile(file, newContents);

می توانید رد شوید --allow-write یا کمی دقیق تر با --allow-write=.، به این معنی که این ابزار فقط اجازه نوشتن پرونده ها را در فهرست فعلی دارد:

$ deno run --allow-read --allow-write=. index.ts --text='readTextFile' --extension=ts --replace='jackWasHere'
/home/jack/git/deno-file-search/index.ts
=> 74 const contents = await Deno.readTextFile(file);

تدوین به یک قابل اجرا

اکنون که اسکریپت خود را داریم و آماده به اشتراک گذاری آن هستیم ، بیایید از Deno بخواهیم تا ابزار ما را به صورت یک اجرا درآورد. به این ترتیب ، کاربران نهایی ما مجبور به اجرای Deno نخواهند بود و مجبور نیستند هر بار همه پرچم های مربوطه را رد کنند. ما می توانیم این کار را هنگام بسته بندی انجام دهیم. deno compile اجازه دهید این کار را انجام دهیم:

$ deno compile --allow-read --allow-write=. index.ts
Check file:///home/jack/git/deno-file-search/index.ts
Bundle file:///home/jack/git/deno-file-search/index.ts
Compile file:///home/jack/git/deno-file-search/index.ts
Emit deno-file-search

و سپس می توانیم اجرایی را فراخوانی کنیم:

$ ./deno-file-search index.ts --text=readTextFile --extension=ts
/home/jack/git/deno-file-search/index.ts
=> 74 const contents = await Deno.readTextFile(file);

من این روش را خیلی دوست دارم. ما می توانیم این ابزار را بسته بندی کنیم بنابراین کاربران ما مجبور نیستند چیزی را کامپایل کنند ، و با ارائه مجوزهای پیش رو منظور ما از این است که کاربران مجبور نیستند. البته این یک معامله است. برخی از کاربران ممکن است بخواهند مجوزهایی را ارائه دهند تا دانش کاملی از آنچه که اسکریپت ما می تواند انجام دهد و نمی تواند انجام دهد ، داشته باشیم ، اما من فکر می کنم بیشتر اوقات خوب است که مجوزها را در قسمت اجرایی تغذیه کنید.

نتیجه

من واقعاً خیلی لذت می برم که در Deno کار می کنم. در مقایسه با Node ، این واقعیت را دوست دارم که TypeScript ، Deno Format و سایر ابزارها تازه از جعبه بیرون می آیند. من نیازی به راه اندازی پروژه Node خود و سپس Prettier ندارم و سپس بهترین روش برای افزودن TypeScript به آن را پیدا می کنم.

Deno (جای تعجب نیست) به اندازه Node صیقل خورده یا گوشتی نیست. بسیاری از بسته های شخص ثالثی که در Node وجود دارند ، معادل Deno خوبی ندارند (اگرچه انتظار دارم این تغییر در زمان انجام شود) ، و یافتن اسناد در برخی موارد بسیار دشوار است. اما اینها همه مشکلات کوچکی است که شما از هر محیط و زبان برنامه نویسی نسبتاً جدیدی انتظار دارید. من اکیداً توصیه می کنم Deno را جستجو کنید و به آن فرصت دهید. قطعاً اینجاست که بماند.

SitePoint دارای لیست در حال رشد مقالات در مورد Deno است. اگر می خواهید در Deno بیشتر کاوش کنید ، آنها را اینجا بررسی کنید.